node-opcua-server 2.76.0 → 2.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base_server.d.ts +110 -110
- package/dist/base_server.js +473 -473
- package/dist/factory.d.ts +12 -12
- package/dist/factory.js +23 -23
- package/dist/factory.js.map +1 -1
- package/dist/filter/check_where_clause_on_address_space.d.ts +3 -3
- package/dist/filter/check_where_clause_on_address_space.js +22 -22
- package/dist/filter/extract_event_fields.d.ts +10 -10
- package/dist/filter/extract_event_fields.js +17 -17
- package/dist/helper.d.ts +10 -10
- package/dist/helper.js +75 -75
- package/dist/history_server_capabilities.d.ts +35 -35
- package/dist/history_server_capabilities.js +43 -43
- package/dist/i_channel_data.d.ts +13 -13
- package/dist/i_channel_data.js +2 -2
- package/dist/i_register_server_manager.d.ts +16 -16
- package/dist/i_register_server_manager.js +2 -2
- package/dist/i_server_side_publish_engine.d.ts +36 -36
- package/dist/i_server_side_publish_engine.js +49 -49
- package/dist/i_socket_data.d.ts +11 -11
- package/dist/i_socket_data.js +2 -2
- package/dist/index.d.ts +16 -16
- package/dist/index.js +32 -32
- package/dist/monitored_item.d.ts +177 -177
- package/dist/monitored_item.js +1001 -1001
- package/dist/node_sampler.d.ts +3 -3
- package/dist/node_sampler.js +75 -75
- package/dist/opcua_server.d.ts +747 -747
- package/dist/opcua_server.js +2431 -2431
- package/dist/queue.d.ts +11 -11
- package/dist/queue.js +71 -71
- package/dist/register_server_manager.d.ts +96 -96
- package/dist/register_server_manager.js +584 -584
- package/dist/register_server_manager_hidden.d.ts +17 -17
- package/dist/register_server_manager_hidden.js +27 -27
- package/dist/register_server_manager_mdns_only.d.ts +22 -22
- package/dist/register_server_manager_mdns_only.js +55 -55
- package/dist/server_capabilities.d.ts +148 -148
- package/dist/server_capabilities.js +92 -92
- package/dist/server_end_point.d.ts +183 -183
- package/dist/server_end_point.js +819 -817
- package/dist/server_end_point.js.map +1 -1
- package/dist/server_engine.d.ts +317 -317
- package/dist/server_engine.js +1716 -1716
- package/dist/server_publish_engine.d.ts +113 -113
- package/dist/server_publish_engine.js +541 -541
- package/dist/server_publish_engine_for_orphan_subscriptions.d.ts +16 -16
- package/dist/server_publish_engine_for_orphan_subscriptions.js +51 -51
- package/dist/server_session.d.ts +182 -182
- package/dist/server_session.js +739 -739
- package/dist/server_subscription.d.ts +421 -421
- package/dist/server_subscription.js +1346 -1346
- package/dist/sessions_compatible_for_transfer.d.ts +2 -2
- package/dist/sessions_compatible_for_transfer.js +39 -39
- package/dist/user_manager.d.ts +32 -32
- package/dist/user_manager.js +74 -74
- package/dist/user_manager_ua.d.ts +3 -3
- package/dist/user_manager_ua.js +39 -39
- package/dist/validate_filter.d.ts +5 -5
- package/dist/validate_filter.js +60 -60
- package/package.json +48 -47
- package/source/factory.ts +2 -2
- package/source/server_end_point.ts +1 -0
package/dist/base_server.js
CHANGED
|
@@ -1,474 +1,474 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.OPCUABaseServer = void 0;
|
|
13
|
-
/**
|
|
14
|
-
* @module node-opcua-server
|
|
15
|
-
*/
|
|
16
|
-
// tslint:disable:no-console
|
|
17
|
-
const fs = require("fs");
|
|
18
|
-
const path = require("path");
|
|
19
|
-
const os = require("os");
|
|
20
|
-
const async = require("async");
|
|
21
|
-
const chalk = require("chalk");
|
|
22
|
-
const global_mutex_1 = require("@ster5/global-mutex");
|
|
23
|
-
const node_opcua_assert_1 = require("node-opcua-assert");
|
|
24
|
-
const node_opcua_certificate_manager_1 = require("node-opcua-certificate-manager");
|
|
25
|
-
const node_opcua_common_1 = require("node-opcua-common");
|
|
26
|
-
const node_opcua_data_model_1 = require("node-opcua-data-model");
|
|
27
|
-
const node_opcua_date_time_1 = require("node-opcua-date-time");
|
|
28
|
-
const node_opcua_debug_1 = require("node-opcua-debug");
|
|
29
|
-
const node_opcua_debug_2 = require("node-opcua-debug");
|
|
30
|
-
const node_opcua_hostname_1 = require("node-opcua-hostname");
|
|
31
|
-
const node_opcua_service_discovery_1 = require("node-opcua-service-discovery");
|
|
32
|
-
const node_opcua_service_endpoints_1 = require("node-opcua-service-endpoints");
|
|
33
|
-
const node_opcua_service_endpoints_2 = require("node-opcua-service-endpoints");
|
|
34
|
-
const node_opcua_service_secure_channel_1 = require("node-opcua-service-secure-channel");
|
|
35
|
-
const node_opcua_status_code_1 = require("node-opcua-status-code");
|
|
36
|
-
const node_opcua_utils_1 = require("node-opcua-utils");
|
|
37
|
-
const node_opcua_client_1 = require("node-opcua-client");
|
|
38
|
-
const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
|
|
39
|
-
const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
|
|
40
|
-
const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
|
|
41
|
-
const warningLog = errorLog;
|
|
42
|
-
function constructFilename(p) {
|
|
43
|
-
let filename = path.join(__dirname, "..", p);
|
|
44
|
-
if (!fs.existsSync(filename)) {
|
|
45
|
-
// try one level up
|
|
46
|
-
filename = path.join(__dirname, p);
|
|
47
|
-
if (!fs.existsSync(filename)) {
|
|
48
|
-
throw new Error("Cannot find filename " + filename + " ( __dirname = " + __dirname);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return filename;
|
|
52
|
-
}
|
|
53
|
-
const default_server_info = {
|
|
54
|
-
// The globally unique identifier for the application instance. This URI is used as
|
|
55
|
-
// ServerUri in Services if the application is a Server.
|
|
56
|
-
applicationUri: (0, node_opcua_common_1.makeApplicationUrn)(os.hostname(), "NodeOPCUA-Server"),
|
|
57
|
-
// The globally unique identifier for the product.
|
|
58
|
-
productUri: "NodeOPCUA-Server",
|
|
59
|
-
// A localized descriptive name for the application.
|
|
60
|
-
applicationName: { text: "NodeOPCUA", locale: "en" },
|
|
61
|
-
applicationType: node_opcua_service_endpoints_1.ApplicationType.Server,
|
|
62
|
-
gatewayServerUri: "",
|
|
63
|
-
discoveryProfileUri: "",
|
|
64
|
-
discoveryUrls: []
|
|
65
|
-
};
|
|
66
|
-
function cleanupEndpoint(endpoint) {
|
|
67
|
-
if (endpoint._on_new_channel) {
|
|
68
|
-
(0, node_opcua_assert_1.assert)(typeof endpoint._on_new_channel === "function");
|
|
69
|
-
endpoint.removeListener("newChannel", endpoint._on_new_channel);
|
|
70
|
-
endpoint._on_new_channel = undefined;
|
|
71
|
-
}
|
|
72
|
-
if (endpoint._on_close_channel) {
|
|
73
|
-
(0, node_opcua_assert_1.assert)(typeof endpoint._on_close_channel === "function");
|
|
74
|
-
endpoint.removeListener("closeChannel", endpoint._on_close_channel);
|
|
75
|
-
endpoint._on_close_channel = undefined;
|
|
76
|
-
}
|
|
77
|
-
if (endpoint._on_connectionRefused) {
|
|
78
|
-
(0, node_opcua_assert_1.assert)(typeof endpoint._on_connectionRefused === "function");
|
|
79
|
-
endpoint.removeListener("connectionRefused", endpoint._on_connectionRefused);
|
|
80
|
-
endpoint._on_connectionRefused = undefined;
|
|
81
|
-
}
|
|
82
|
-
if (endpoint._on_openSecureChannelFailure) {
|
|
83
|
-
(0, node_opcua_assert_1.assert)(typeof endpoint._on_openSecureChannelFailure === "function");
|
|
84
|
-
endpoint.removeListener("openSecureChannelFailure", endpoint._on_openSecureChannelFailure);
|
|
85
|
-
endpoint._on_openSecureChannelFailure = undefined;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const emptyCallback = () => {
|
|
89
|
-
/* empty */
|
|
90
|
-
};
|
|
91
|
-
/**
|
|
92
|
-
* @class OPCUABaseServer
|
|
93
|
-
* @constructor
|
|
94
|
-
*/
|
|
95
|
-
class OPCUABaseServer extends node_opcua_common_1.OPCUASecureObject {
|
|
96
|
-
constructor(options) {
|
|
97
|
-
var _a, _b;
|
|
98
|
-
options = options || {};
|
|
99
|
-
if (!options.serverCertificateManager) {
|
|
100
|
-
options.serverCertificateManager = (0, node_opcua_certificate_manager_1.getDefaultCertificateManager)("PKI");
|
|
101
|
-
}
|
|
102
|
-
options.privateKeyFile = options.privateKeyFile || options.serverCertificateManager.privateKey;
|
|
103
|
-
options.certificateFile =
|
|
104
|
-
options.certificateFile || path.join(options.serverCertificateManager.rootDir, "own/certs/certificate.pem");
|
|
105
|
-
super(options);
|
|
106
|
-
this.serverCertificateManager = options.serverCertificateManager;
|
|
107
|
-
this.capabilitiesForMDNS = [];
|
|
108
|
-
this.endpoints = [];
|
|
109
|
-
this.options = options;
|
|
110
|
-
this._preInitTask = [];
|
|
111
|
-
const serverInfo = Object.assign(Object.assign({}, default_server_info), options.serverInfo);
|
|
112
|
-
serverInfo.applicationName = (0, node_opcua_data_model_1.coerceLocalizedText)(serverInfo.applicationName);
|
|
113
|
-
this.serverInfo = new node_opcua_service_endpoints_2.ApplicationDescription(serverInfo);
|
|
114
|
-
if (this.serverInfo.applicationName.toString().match(/urn:/)) {
|
|
115
|
-
errorLog("[NODE-OPCUA-E06] application name cannot be a urn", this.serverInfo.applicationName.toString());
|
|
116
|
-
}
|
|
117
|
-
this.serverInfo.applicationName.locale = ((_a = this.serverInfo.applicationName) === null || _a === void 0 ? void 0 : _a.locale) || "en";
|
|
118
|
-
if (!((_b = this.serverInfo.applicationName) === null || _b === void 0 ? void 0 : _b.locale)) {
|
|
119
|
-
warningLog("[NODE-OPCUA-W24] the server applicationName must have a valid locale : ", this.serverInfo.applicationName.toString());
|
|
120
|
-
}
|
|
121
|
-
const __applicationUri = serverInfo.applicationUri || "";
|
|
122
|
-
this.serverInfo.__defineGetter__("applicationUri", () => (0, node_opcua_hostname_1.resolveFullyQualifiedDomainName)(__applicationUri));
|
|
123
|
-
this._preInitTask.push(() => __awaiter(this, void 0, void 0, function* () {
|
|
124
|
-
const fqdn = yield (0, node_opcua_hostname_1.extractFullyQualifiedDomainName)();
|
|
125
|
-
}));
|
|
126
|
-
this._preInitTask.push(() => __awaiter(this, void 0, void 0, function* () {
|
|
127
|
-
yield this.initializeCM();
|
|
128
|
-
}));
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* The type of server
|
|
132
|
-
*/
|
|
133
|
-
get serverType() {
|
|
134
|
-
return this.serverInfo.applicationType;
|
|
135
|
-
}
|
|
136
|
-
createDefaultCertificate() {
|
|
137
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
-
if (fs.existsSync(this.certificateFile)) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
const lockfile = path.join(this.certificateFile + ".lock");
|
|
142
|
-
yield (0, global_mutex_1.withLock)({ lockfile }, () => __awaiter(this, void 0, void 0, function* () {
|
|
143
|
-
if (!fs.existsSync(this.certificateFile)) {
|
|
144
|
-
const applicationUri = this.serverInfo.applicationUri;
|
|
145
|
-
const hostname = (0, node_opcua_hostname_1.getHostname)();
|
|
146
|
-
yield this.serverCertificateManager.createSelfSignedCertificate({
|
|
147
|
-
applicationUri,
|
|
148
|
-
dns: [hostname],
|
|
149
|
-
// ip: await getIpAddresses(),
|
|
150
|
-
outputFile: this.certificateFile,
|
|
151
|
-
subject: (0, node_opcua_certificate_manager_1.makeSubject)(this.serverInfo.applicationName.text, hostname),
|
|
152
|
-
startDate: new Date(),
|
|
153
|
-
validity: 365 * 10 // 10 years
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}));
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
initializeCM() {
|
|
160
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
-
yield this.serverCertificateManager.initialize();
|
|
162
|
-
yield this.createDefaultCertificate();
|
|
163
|
-
debugLog("privateKey = ", this.privateKeyFile, this.serverCertificateManager.privateKey);
|
|
164
|
-
debugLog("certificateFile = ", this.certificateFile);
|
|
165
|
-
yield node_opcua_client_1.performCertificateSanityCheck.call(this, "server", this.serverCertificateManager, this.serverInfo.applicationUri);
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* start all registered endPoint, in parallel, and call done when all endPoints are listening.
|
|
170
|
-
* @method start
|
|
171
|
-
* @async
|
|
172
|
-
* @param {callback} done
|
|
173
|
-
*/
|
|
174
|
-
start(done) {
|
|
175
|
-
(0, node_opcua_assert_1.assert)(typeof done === "function");
|
|
176
|
-
this.startAsync()
|
|
177
|
-
.then(() => done(null))
|
|
178
|
-
.catch((err) => done(err));
|
|
179
|
-
}
|
|
180
|
-
performPreInitialization() {
|
|
181
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
-
const tasks = this._preInitTask;
|
|
183
|
-
this._preInitTask = [];
|
|
184
|
-
for (const task of tasks) {
|
|
185
|
-
yield task();
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
startAsync() {
|
|
190
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
191
|
-
yield this.performPreInitialization();
|
|
192
|
-
(0, node_opcua_assert_1.assert)(Array.isArray(this.endpoints));
|
|
193
|
-
(0, node_opcua_assert_1.assert)(this.endpoints.length > 0, "We need at least one end point");
|
|
194
|
-
(0, node_opcua_date_time_1.installPeriodicClockAdjustment)();
|
|
195
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
196
|
-
const server = this;
|
|
197
|
-
const _on_new_channel = function (channel) {
|
|
198
|
-
server.emit("newChannel", channel, this);
|
|
199
|
-
};
|
|
200
|
-
const _on_close_channel = function (channel) {
|
|
201
|
-
server.emit("closeChannel", channel, this);
|
|
202
|
-
};
|
|
203
|
-
const _on_connectionRefused = function (socketData) {
|
|
204
|
-
server.emit("connectionRefused", socketData, this);
|
|
205
|
-
};
|
|
206
|
-
const _on_openSecureChannelFailure = function (socketData, channelData) {
|
|
207
|
-
server.emit("openSecureChannelFailure", socketData, channelData, this);
|
|
208
|
-
};
|
|
209
|
-
const promises = [];
|
|
210
|
-
for (const endpoint of this.endpoints) {
|
|
211
|
-
(0, node_opcua_assert_1.assert)(!endpoint._on_close_channel);
|
|
212
|
-
endpoint._on_new_channel = _on_new_channel;
|
|
213
|
-
endpoint.on("newChannel", endpoint._on_new_channel);
|
|
214
|
-
endpoint._on_close_channel = _on_close_channel;
|
|
215
|
-
endpoint.on("closeChannel", endpoint._on_close_channel);
|
|
216
|
-
endpoint._on_connectionRefused = _on_connectionRefused;
|
|
217
|
-
endpoint.on("connectionRefused", endpoint._on_connectionRefused);
|
|
218
|
-
endpoint._on_openSecureChannelFailure = _on_openSecureChannelFailure;
|
|
219
|
-
endpoint.on("openSecureChannelFailure", endpoint._on_openSecureChannelFailure);
|
|
220
|
-
promises.push(new Promise((resolve, reject) => endpoint.start((err) => (err ? reject(err) : resolve()))));
|
|
221
|
-
}
|
|
222
|
-
yield Promise.all(promises);
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* shutdown all server endPoints
|
|
227
|
-
* @async
|
|
228
|
-
*/
|
|
229
|
-
shutdown(done) {
|
|
230
|
-
(0, node_opcua_assert_1.assert)(typeof done === "function");
|
|
231
|
-
(0, node_opcua_date_time_1.uninstallPeriodicClockAdjustment)();
|
|
232
|
-
this.serverCertificateManager.dispose().then(() => {
|
|
233
|
-
debugLog("OPCUABaseServer#shutdown starting");
|
|
234
|
-
async.forEach(this.endpoints, (endpoint, callback) => {
|
|
235
|
-
cleanupEndpoint(endpoint);
|
|
236
|
-
endpoint.shutdown(callback);
|
|
237
|
-
}, (err) => {
|
|
238
|
-
debugLog("shutdown completed");
|
|
239
|
-
done(err);
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
shutdownChannels(callback) {
|
|
244
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
245
|
-
debugLog("OPCUABaseServer#shutdownChannels");
|
|
246
|
-
async.forEach(this.endpoints, (endpoint, inner_callback) => {
|
|
247
|
-
debugLog(" shutting down endpoint ", endpoint.endpointDescriptions()[0].endpointUrl);
|
|
248
|
-
async.series([
|
|
249
|
-
// xx (callback2: (err?: Error| null) => void) => {
|
|
250
|
-
// xx endpoint.suspendConnection(callback2);
|
|
251
|
-
// xx },
|
|
252
|
-
(callback2) => {
|
|
253
|
-
endpoint.abruptlyInterruptChannels();
|
|
254
|
-
endpoint.shutdown(callback2);
|
|
255
|
-
}
|
|
256
|
-
// xx (callback2: (err?: Error| null) => void) => {
|
|
257
|
-
// xx endpoint.restoreConnection(callback2);
|
|
258
|
-
// xx }
|
|
259
|
-
], inner_callback);
|
|
260
|
-
}, callback);
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* @private
|
|
264
|
-
*/
|
|
265
|
-
on_request(message, channel) {
|
|
266
|
-
(0, node_opcua_assert_1.assert)(message.request);
|
|
267
|
-
(0, node_opcua_assert_1.assert)(message.requestId !== 0);
|
|
268
|
-
const request = message.request;
|
|
269
|
-
// install channel._on_response so we can intercept its call and emit the "response" event.
|
|
270
|
-
if (!channel._on_response) {
|
|
271
|
-
channel._on_response = (msg, response1 /*, inner_message: Message*/) => {
|
|
272
|
-
this.emit("response", response1, channel);
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
// prepare request
|
|
276
|
-
this.prepare(message, channel);
|
|
277
|
-
if (doDebug) {
|
|
278
|
-
debugLog(chalk.green.bold("--------------------------------------------------------"), channel.channelId, request.schema.name);
|
|
279
|
-
}
|
|
280
|
-
let errMessage;
|
|
281
|
-
let response;
|
|
282
|
-
this.emit("request", request, channel);
|
|
283
|
-
try {
|
|
284
|
-
// handler must be named _on_ActionRequest()
|
|
285
|
-
const handler = this["_on_" + request.schema.name];
|
|
286
|
-
if (typeof handler === "function") {
|
|
287
|
-
// eslint-disable-next-line prefer-rest-params
|
|
288
|
-
handler.apply(this, arguments);
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
errMessage = "[NODE-OPCUA-W07] Unsupported Service : " + request.schema.name;
|
|
292
|
-
warningLog(errMessage);
|
|
293
|
-
debugLog(chalk.red.bold(errMessage));
|
|
294
|
-
response = makeServiceFault(node_opcua_status_code_1.StatusCodes.BadServiceUnsupported, [errMessage]);
|
|
295
|
-
channel.send_response("MSG", response, message, emptyCallback);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
catch (err) {
|
|
299
|
-
/* istanbul ignore if */
|
|
300
|
-
const errMessage1 = "[NODE-OPCUA-W08] EXCEPTION CAUGHT WHILE PROCESSING REQUEST !! " + request.schema.name;
|
|
301
|
-
warningLog(chalk.red.bold(errMessage1));
|
|
302
|
-
warningLog(request.toString());
|
|
303
|
-
(0, node_opcua_debug_2.displayTraceFromThisProjectOnly)(err);
|
|
304
|
-
let additional_messages = [];
|
|
305
|
-
additional_messages.push("EXCEPTION CAUGHT WHILE PROCESSING REQUEST !!! " + request.schema.name);
|
|
306
|
-
if (err instanceof Error) {
|
|
307
|
-
additional_messages.push(err.message);
|
|
308
|
-
if (err.stack) {
|
|
309
|
-
additional_messages = additional_messages.concat(err.stack.split("\n"));
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
response = makeServiceFault(node_opcua_status_code_1.StatusCodes.BadInternalError, additional_messages);
|
|
313
|
-
channel.send_response("MSG", response, message, emptyCallback);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* @private
|
|
318
|
-
*/
|
|
319
|
-
_get_endpoints(endpointUrl) {
|
|
320
|
-
let endpoints = [];
|
|
321
|
-
for (const endPoint of this.endpoints) {
|
|
322
|
-
const ep = endPoint.endpointDescriptions();
|
|
323
|
-
const epFiltered = endpointUrl ? ep.filter((e) => (0, node_opcua_utils_1.matchUri)(e.endpointUrl, endpointUrl)) : ep;
|
|
324
|
-
endpoints = endpoints.concat(epFiltered);
|
|
325
|
-
}
|
|
326
|
-
return endpoints;
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* get one of the possible endpointUrl
|
|
330
|
-
*/
|
|
331
|
-
getEndpointUrl() {
|
|
332
|
-
return this._get_endpoints()[0].endpointUrl;
|
|
333
|
-
}
|
|
334
|
-
getDiscoveryUrls() {
|
|
335
|
-
const discoveryUrls = this.endpoints.map((e) => {
|
|
336
|
-
return e.endpointDescriptions()[0].endpointUrl;
|
|
337
|
-
});
|
|
338
|
-
return discoveryUrls;
|
|
339
|
-
}
|
|
340
|
-
getServers(channel) {
|
|
341
|
-
this.serverInfo.discoveryUrls = this.getDiscoveryUrls();
|
|
342
|
-
const servers = [this.serverInfo];
|
|
343
|
-
return servers;
|
|
344
|
-
}
|
|
345
|
-
suspendEndPoints(callback) {
|
|
346
|
-
/* istanbul ignore next */
|
|
347
|
-
if (!callback) {
|
|
348
|
-
throw new Error("Internal Error");
|
|
349
|
-
}
|
|
350
|
-
async.forEach(this.endpoints, (ep, _inner_callback) => {
|
|
351
|
-
/* istanbul ignore next */
|
|
352
|
-
if (doDebug) {
|
|
353
|
-
debugLog("Suspending ", ep.endpointDescriptions()[0].endpointUrl);
|
|
354
|
-
}
|
|
355
|
-
ep.suspendConnection((err) => {
|
|
356
|
-
/* istanbul ignore next */
|
|
357
|
-
if (doDebug) {
|
|
358
|
-
debugLog("Suspended ", ep.endpointDescriptions()[0].endpointUrl);
|
|
359
|
-
}
|
|
360
|
-
_inner_callback(err);
|
|
361
|
-
});
|
|
362
|
-
}, (err) => callback(err));
|
|
363
|
-
}
|
|
364
|
-
resumeEndPoints(callback) {
|
|
365
|
-
async.forEach(this.endpoints, (ep, _inner_callback) => {
|
|
366
|
-
ep.restoreConnection(_inner_callback);
|
|
367
|
-
}, (err) => callback(err));
|
|
368
|
-
}
|
|
369
|
-
prepare(message, channel) {
|
|
370
|
-
/* empty */
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* @private
|
|
374
|
-
*/
|
|
375
|
-
_on_GetEndpointsRequest(message, channel) {
|
|
376
|
-
const request = message.request;
|
|
377
|
-
(0, node_opcua_assert_1.assert)(request.schema.name === "GetEndpointsRequest");
|
|
378
|
-
const response = new node_opcua_service_endpoints_1.GetEndpointsResponse({});
|
|
379
|
-
response.endpoints = this._get_endpoints(null);
|
|
380
|
-
response.endpoints = response.endpoints.filter((endpoint) => !endpoint.restricted);
|
|
381
|
-
// apply filters
|
|
382
|
-
if (request.profileUris && request.profileUris.length > 0) {
|
|
383
|
-
response.endpoints = response.endpoints.filter((endpoint) => {
|
|
384
|
-
return request.profileUris.indexOf(endpoint.transportProfileUri) >= 0;
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
// adjust locale on ApplicationName to match requested local or provide
|
|
388
|
-
// a string with neutral locale (locale === null)
|
|
389
|
-
// TODO: find a better way to handle this
|
|
390
|
-
response.endpoints.forEach((endpoint) => {
|
|
391
|
-
endpoint.server.applicationName.locale = "en-US";
|
|
392
|
-
});
|
|
393
|
-
channel.send_response("MSG", response, message, emptyCallback);
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* @private
|
|
397
|
-
*/
|
|
398
|
-
_on_FindServersRequest(message, channel) {
|
|
399
|
-
// Release 1.02 13 OPC Unified Architecture, Part 4 :
|
|
400
|
-
// This Service can be used without security and it is therefore vulnerable to Denial Of Service (DOS)
|
|
401
|
-
// attacks. A Server should minimize the amount of processing required to send the response for this
|
|
402
|
-
// Service. This can be achieved by preparing the result in advance. The Server should also add a
|
|
403
|
-
// short delay before starting processing of a request during high traffic conditions.
|
|
404
|
-
const shortDelay = 100; // milliseconds
|
|
405
|
-
setTimeout(() => {
|
|
406
|
-
const request = message.request;
|
|
407
|
-
(0, node_opcua_assert_1.assert)(request.schema.name === "FindServersRequest");
|
|
408
|
-
if (!(request instanceof node_opcua_service_discovery_1.FindServersRequest)) {
|
|
409
|
-
throw new Error("Invalid request type");
|
|
410
|
-
}
|
|
411
|
-
let servers = this.getServers(channel);
|
|
412
|
-
// apply filters
|
|
413
|
-
// TODO /
|
|
414
|
-
if (request.serverUris && request.serverUris.length > 0) {
|
|
415
|
-
// A serverUri matches the applicationUri from the ApplicationDescription define
|
|
416
|
-
servers = servers.filter((inner_Server) => {
|
|
417
|
-
return request.serverUris.indexOf(inner_Server.applicationUri) >= 0;
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
function adapt(applicationDescription) {
|
|
421
|
-
return new node_opcua_service_endpoints_2.ApplicationDescription({
|
|
422
|
-
applicationName: applicationDescription.applicationName,
|
|
423
|
-
applicationType: applicationDescription.applicationType,
|
|
424
|
-
applicationUri: applicationDescription.applicationUri,
|
|
425
|
-
discoveryProfileUri: applicationDescription.discoveryProfileUri,
|
|
426
|
-
discoveryUrls: applicationDescription.discoveryUrls,
|
|
427
|
-
gatewayServerUri: applicationDescription.gatewayServerUri,
|
|
428
|
-
productUri: applicationDescription.productUri
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
const response = new node_opcua_service_discovery_1.FindServersResponse({
|
|
432
|
-
servers: servers.map(adapt)
|
|
433
|
-
});
|
|
434
|
-
channel.send_response("MSG", response, message, emptyCallback);
|
|
435
|
-
}, shortDelay);
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* returns a array of currently active channels
|
|
439
|
-
*/
|
|
440
|
-
getChannels() {
|
|
441
|
-
let channels = [];
|
|
442
|
-
for (const endpoint of this.endpoints) {
|
|
443
|
-
const c = endpoint.getChannels();
|
|
444
|
-
channels = channels.concat(c);
|
|
445
|
-
}
|
|
446
|
-
return channels;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
exports.OPCUABaseServer = OPCUABaseServer;
|
|
450
|
-
OPCUABaseServer.makeServiceFault = makeServiceFault;
|
|
451
|
-
/**
|
|
452
|
-
* construct a service Fault response
|
|
453
|
-
* @method makeServiceFault
|
|
454
|
-
* @param statusCode
|
|
455
|
-
* @param messages
|
|
456
|
-
*/
|
|
457
|
-
function makeServiceFault(statusCode, messages) {
|
|
458
|
-
const response = new node_opcua_service_secure_channel_1.ServiceFault();
|
|
459
|
-
response.responseHeader.serviceResult = statusCode;
|
|
460
|
-
// xx response.serviceDiagnostics.push( new DiagnosticInfo({ additionalInfo: messages.join("\n")}));
|
|
461
|
-
(0, node_opcua_assert_1.assert)(Array.isArray(messages));
|
|
462
|
-
(0, node_opcua_assert_1.assert)(typeof messages[0] === "string");
|
|
463
|
-
response.responseHeader.stringTable = messages;
|
|
464
|
-
// tslint:disable:no-console
|
|
465
|
-
warningLog(chalk.cyan(" messages "), messages.join("\n"));
|
|
466
|
-
return response;
|
|
467
|
-
}
|
|
468
|
-
// tslint:disable:no-var-requires
|
|
469
|
-
const thenify = require("thenify");
|
|
470
|
-
const opts = { multiArgs: false };
|
|
471
|
-
OPCUABaseServer.prototype.resumeEndPoints = thenify.withCallback(OPCUABaseServer.prototype.resumeEndPoints, opts);
|
|
472
|
-
OPCUABaseServer.prototype.suspendEndPoints = thenify.withCallback(OPCUABaseServer.prototype.suspendEndPoints, opts);
|
|
473
|
-
OPCUABaseServer.prototype.shutdownChannels = thenify.withCallback(OPCUABaseServer.prototype.shutdownChannels, opts);
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.OPCUABaseServer = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* @module node-opcua-server
|
|
15
|
+
*/
|
|
16
|
+
// tslint:disable:no-console
|
|
17
|
+
const fs = require("fs");
|
|
18
|
+
const path = require("path");
|
|
19
|
+
const os = require("os");
|
|
20
|
+
const async = require("async");
|
|
21
|
+
const chalk = require("chalk");
|
|
22
|
+
const global_mutex_1 = require("@ster5/global-mutex");
|
|
23
|
+
const node_opcua_assert_1 = require("node-opcua-assert");
|
|
24
|
+
const node_opcua_certificate_manager_1 = require("node-opcua-certificate-manager");
|
|
25
|
+
const node_opcua_common_1 = require("node-opcua-common");
|
|
26
|
+
const node_opcua_data_model_1 = require("node-opcua-data-model");
|
|
27
|
+
const node_opcua_date_time_1 = require("node-opcua-date-time");
|
|
28
|
+
const node_opcua_debug_1 = require("node-opcua-debug");
|
|
29
|
+
const node_opcua_debug_2 = require("node-opcua-debug");
|
|
30
|
+
const node_opcua_hostname_1 = require("node-opcua-hostname");
|
|
31
|
+
const node_opcua_service_discovery_1 = require("node-opcua-service-discovery");
|
|
32
|
+
const node_opcua_service_endpoints_1 = require("node-opcua-service-endpoints");
|
|
33
|
+
const node_opcua_service_endpoints_2 = require("node-opcua-service-endpoints");
|
|
34
|
+
const node_opcua_service_secure_channel_1 = require("node-opcua-service-secure-channel");
|
|
35
|
+
const node_opcua_status_code_1 = require("node-opcua-status-code");
|
|
36
|
+
const node_opcua_utils_1 = require("node-opcua-utils");
|
|
37
|
+
const node_opcua_client_1 = require("node-opcua-client");
|
|
38
|
+
const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
|
|
39
|
+
const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
|
|
40
|
+
const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
|
|
41
|
+
const warningLog = errorLog;
|
|
42
|
+
function constructFilename(p) {
|
|
43
|
+
let filename = path.join(__dirname, "..", p);
|
|
44
|
+
if (!fs.existsSync(filename)) {
|
|
45
|
+
// try one level up
|
|
46
|
+
filename = path.join(__dirname, p);
|
|
47
|
+
if (!fs.existsSync(filename)) {
|
|
48
|
+
throw new Error("Cannot find filename " + filename + " ( __dirname = " + __dirname);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return filename;
|
|
52
|
+
}
|
|
53
|
+
const default_server_info = {
|
|
54
|
+
// The globally unique identifier for the application instance. This URI is used as
|
|
55
|
+
// ServerUri in Services if the application is a Server.
|
|
56
|
+
applicationUri: (0, node_opcua_common_1.makeApplicationUrn)(os.hostname(), "NodeOPCUA-Server"),
|
|
57
|
+
// The globally unique identifier for the product.
|
|
58
|
+
productUri: "NodeOPCUA-Server",
|
|
59
|
+
// A localized descriptive name for the application.
|
|
60
|
+
applicationName: { text: "NodeOPCUA", locale: "en" },
|
|
61
|
+
applicationType: node_opcua_service_endpoints_1.ApplicationType.Server,
|
|
62
|
+
gatewayServerUri: "",
|
|
63
|
+
discoveryProfileUri: "",
|
|
64
|
+
discoveryUrls: []
|
|
65
|
+
};
|
|
66
|
+
function cleanupEndpoint(endpoint) {
|
|
67
|
+
if (endpoint._on_new_channel) {
|
|
68
|
+
(0, node_opcua_assert_1.assert)(typeof endpoint._on_new_channel === "function");
|
|
69
|
+
endpoint.removeListener("newChannel", endpoint._on_new_channel);
|
|
70
|
+
endpoint._on_new_channel = undefined;
|
|
71
|
+
}
|
|
72
|
+
if (endpoint._on_close_channel) {
|
|
73
|
+
(0, node_opcua_assert_1.assert)(typeof endpoint._on_close_channel === "function");
|
|
74
|
+
endpoint.removeListener("closeChannel", endpoint._on_close_channel);
|
|
75
|
+
endpoint._on_close_channel = undefined;
|
|
76
|
+
}
|
|
77
|
+
if (endpoint._on_connectionRefused) {
|
|
78
|
+
(0, node_opcua_assert_1.assert)(typeof endpoint._on_connectionRefused === "function");
|
|
79
|
+
endpoint.removeListener("connectionRefused", endpoint._on_connectionRefused);
|
|
80
|
+
endpoint._on_connectionRefused = undefined;
|
|
81
|
+
}
|
|
82
|
+
if (endpoint._on_openSecureChannelFailure) {
|
|
83
|
+
(0, node_opcua_assert_1.assert)(typeof endpoint._on_openSecureChannelFailure === "function");
|
|
84
|
+
endpoint.removeListener("openSecureChannelFailure", endpoint._on_openSecureChannelFailure);
|
|
85
|
+
endpoint._on_openSecureChannelFailure = undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const emptyCallback = () => {
|
|
89
|
+
/* empty */
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* @class OPCUABaseServer
|
|
93
|
+
* @constructor
|
|
94
|
+
*/
|
|
95
|
+
class OPCUABaseServer extends node_opcua_common_1.OPCUASecureObject {
|
|
96
|
+
constructor(options) {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
options = options || {};
|
|
99
|
+
if (!options.serverCertificateManager) {
|
|
100
|
+
options.serverCertificateManager = (0, node_opcua_certificate_manager_1.getDefaultCertificateManager)("PKI");
|
|
101
|
+
}
|
|
102
|
+
options.privateKeyFile = options.privateKeyFile || options.serverCertificateManager.privateKey;
|
|
103
|
+
options.certificateFile =
|
|
104
|
+
options.certificateFile || path.join(options.serverCertificateManager.rootDir, "own/certs/certificate.pem");
|
|
105
|
+
super(options);
|
|
106
|
+
this.serverCertificateManager = options.serverCertificateManager;
|
|
107
|
+
this.capabilitiesForMDNS = [];
|
|
108
|
+
this.endpoints = [];
|
|
109
|
+
this.options = options;
|
|
110
|
+
this._preInitTask = [];
|
|
111
|
+
const serverInfo = Object.assign(Object.assign({}, default_server_info), options.serverInfo);
|
|
112
|
+
serverInfo.applicationName = (0, node_opcua_data_model_1.coerceLocalizedText)(serverInfo.applicationName);
|
|
113
|
+
this.serverInfo = new node_opcua_service_endpoints_2.ApplicationDescription(serverInfo);
|
|
114
|
+
if (this.serverInfo.applicationName.toString().match(/urn:/)) {
|
|
115
|
+
errorLog("[NODE-OPCUA-E06] application name cannot be a urn", this.serverInfo.applicationName.toString());
|
|
116
|
+
}
|
|
117
|
+
this.serverInfo.applicationName.locale = ((_a = this.serverInfo.applicationName) === null || _a === void 0 ? void 0 : _a.locale) || "en";
|
|
118
|
+
if (!((_b = this.serverInfo.applicationName) === null || _b === void 0 ? void 0 : _b.locale)) {
|
|
119
|
+
warningLog("[NODE-OPCUA-W24] the server applicationName must have a valid locale : ", this.serverInfo.applicationName.toString());
|
|
120
|
+
}
|
|
121
|
+
const __applicationUri = serverInfo.applicationUri || "";
|
|
122
|
+
this.serverInfo.__defineGetter__("applicationUri", () => (0, node_opcua_hostname_1.resolveFullyQualifiedDomainName)(__applicationUri));
|
|
123
|
+
this._preInitTask.push(() => __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
const fqdn = yield (0, node_opcua_hostname_1.extractFullyQualifiedDomainName)();
|
|
125
|
+
}));
|
|
126
|
+
this._preInitTask.push(() => __awaiter(this, void 0, void 0, function* () {
|
|
127
|
+
yield this.initializeCM();
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The type of server
|
|
132
|
+
*/
|
|
133
|
+
get serverType() {
|
|
134
|
+
return this.serverInfo.applicationType;
|
|
135
|
+
}
|
|
136
|
+
createDefaultCertificate() {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
if (fs.existsSync(this.certificateFile)) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const lockfile = path.join(this.certificateFile + ".lock");
|
|
142
|
+
yield (0, global_mutex_1.withLock)({ lockfile }, () => __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
if (!fs.existsSync(this.certificateFile)) {
|
|
144
|
+
const applicationUri = this.serverInfo.applicationUri;
|
|
145
|
+
const hostname = (0, node_opcua_hostname_1.getHostname)();
|
|
146
|
+
yield this.serverCertificateManager.createSelfSignedCertificate({
|
|
147
|
+
applicationUri,
|
|
148
|
+
dns: [hostname],
|
|
149
|
+
// ip: await getIpAddresses(),
|
|
150
|
+
outputFile: this.certificateFile,
|
|
151
|
+
subject: (0, node_opcua_certificate_manager_1.makeSubject)(this.serverInfo.applicationName.text, hostname),
|
|
152
|
+
startDate: new Date(),
|
|
153
|
+
validity: 365 * 10 // 10 years
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}));
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
initializeCM() {
|
|
160
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
+
yield this.serverCertificateManager.initialize();
|
|
162
|
+
yield this.createDefaultCertificate();
|
|
163
|
+
debugLog("privateKey = ", this.privateKeyFile, this.serverCertificateManager.privateKey);
|
|
164
|
+
debugLog("certificateFile = ", this.certificateFile);
|
|
165
|
+
yield node_opcua_client_1.performCertificateSanityCheck.call(this, "server", this.serverCertificateManager, this.serverInfo.applicationUri);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* start all registered endPoint, in parallel, and call done when all endPoints are listening.
|
|
170
|
+
* @method start
|
|
171
|
+
* @async
|
|
172
|
+
* @param {callback} done
|
|
173
|
+
*/
|
|
174
|
+
start(done) {
|
|
175
|
+
(0, node_opcua_assert_1.assert)(typeof done === "function");
|
|
176
|
+
this.startAsync()
|
|
177
|
+
.then(() => done(null))
|
|
178
|
+
.catch((err) => done(err));
|
|
179
|
+
}
|
|
180
|
+
performPreInitialization() {
|
|
181
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
+
const tasks = this._preInitTask;
|
|
183
|
+
this._preInitTask = [];
|
|
184
|
+
for (const task of tasks) {
|
|
185
|
+
yield task();
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
startAsync() {
|
|
190
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
191
|
+
yield this.performPreInitialization();
|
|
192
|
+
(0, node_opcua_assert_1.assert)(Array.isArray(this.endpoints));
|
|
193
|
+
(0, node_opcua_assert_1.assert)(this.endpoints.length > 0, "We need at least one end point");
|
|
194
|
+
(0, node_opcua_date_time_1.installPeriodicClockAdjustment)();
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
196
|
+
const server = this;
|
|
197
|
+
const _on_new_channel = function (channel) {
|
|
198
|
+
server.emit("newChannel", channel, this);
|
|
199
|
+
};
|
|
200
|
+
const _on_close_channel = function (channel) {
|
|
201
|
+
server.emit("closeChannel", channel, this);
|
|
202
|
+
};
|
|
203
|
+
const _on_connectionRefused = function (socketData) {
|
|
204
|
+
server.emit("connectionRefused", socketData, this);
|
|
205
|
+
};
|
|
206
|
+
const _on_openSecureChannelFailure = function (socketData, channelData) {
|
|
207
|
+
server.emit("openSecureChannelFailure", socketData, channelData, this);
|
|
208
|
+
};
|
|
209
|
+
const promises = [];
|
|
210
|
+
for (const endpoint of this.endpoints) {
|
|
211
|
+
(0, node_opcua_assert_1.assert)(!endpoint._on_close_channel);
|
|
212
|
+
endpoint._on_new_channel = _on_new_channel;
|
|
213
|
+
endpoint.on("newChannel", endpoint._on_new_channel);
|
|
214
|
+
endpoint._on_close_channel = _on_close_channel;
|
|
215
|
+
endpoint.on("closeChannel", endpoint._on_close_channel);
|
|
216
|
+
endpoint._on_connectionRefused = _on_connectionRefused;
|
|
217
|
+
endpoint.on("connectionRefused", endpoint._on_connectionRefused);
|
|
218
|
+
endpoint._on_openSecureChannelFailure = _on_openSecureChannelFailure;
|
|
219
|
+
endpoint.on("openSecureChannelFailure", endpoint._on_openSecureChannelFailure);
|
|
220
|
+
promises.push(new Promise((resolve, reject) => endpoint.start((err) => (err ? reject(err) : resolve()))));
|
|
221
|
+
}
|
|
222
|
+
yield Promise.all(promises);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* shutdown all server endPoints
|
|
227
|
+
* @async
|
|
228
|
+
*/
|
|
229
|
+
shutdown(done) {
|
|
230
|
+
(0, node_opcua_assert_1.assert)(typeof done === "function");
|
|
231
|
+
(0, node_opcua_date_time_1.uninstallPeriodicClockAdjustment)();
|
|
232
|
+
this.serverCertificateManager.dispose().then(() => {
|
|
233
|
+
debugLog("OPCUABaseServer#shutdown starting");
|
|
234
|
+
async.forEach(this.endpoints, (endpoint, callback) => {
|
|
235
|
+
cleanupEndpoint(endpoint);
|
|
236
|
+
endpoint.shutdown(callback);
|
|
237
|
+
}, (err) => {
|
|
238
|
+
debugLog("shutdown completed");
|
|
239
|
+
done(err);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
shutdownChannels(callback) {
|
|
244
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
245
|
+
debugLog("OPCUABaseServer#shutdownChannels");
|
|
246
|
+
async.forEach(this.endpoints, (endpoint, inner_callback) => {
|
|
247
|
+
debugLog(" shutting down endpoint ", endpoint.endpointDescriptions()[0].endpointUrl);
|
|
248
|
+
async.series([
|
|
249
|
+
// xx (callback2: (err?: Error| null) => void) => {
|
|
250
|
+
// xx endpoint.suspendConnection(callback2);
|
|
251
|
+
// xx },
|
|
252
|
+
(callback2) => {
|
|
253
|
+
endpoint.abruptlyInterruptChannels();
|
|
254
|
+
endpoint.shutdown(callback2);
|
|
255
|
+
}
|
|
256
|
+
// xx (callback2: (err?: Error| null) => void) => {
|
|
257
|
+
// xx endpoint.restoreConnection(callback2);
|
|
258
|
+
// xx }
|
|
259
|
+
], inner_callback);
|
|
260
|
+
}, callback);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* @private
|
|
264
|
+
*/
|
|
265
|
+
on_request(message, channel) {
|
|
266
|
+
(0, node_opcua_assert_1.assert)(message.request);
|
|
267
|
+
(0, node_opcua_assert_1.assert)(message.requestId !== 0);
|
|
268
|
+
const request = message.request;
|
|
269
|
+
// install channel._on_response so we can intercept its call and emit the "response" event.
|
|
270
|
+
if (!channel._on_response) {
|
|
271
|
+
channel._on_response = (msg, response1 /*, inner_message: Message*/) => {
|
|
272
|
+
this.emit("response", response1, channel);
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
// prepare request
|
|
276
|
+
this.prepare(message, channel);
|
|
277
|
+
if (doDebug) {
|
|
278
|
+
debugLog(chalk.green.bold("--------------------------------------------------------"), channel.channelId, request.schema.name);
|
|
279
|
+
}
|
|
280
|
+
let errMessage;
|
|
281
|
+
let response;
|
|
282
|
+
this.emit("request", request, channel);
|
|
283
|
+
try {
|
|
284
|
+
// handler must be named _on_ActionRequest()
|
|
285
|
+
const handler = this["_on_" + request.schema.name];
|
|
286
|
+
if (typeof handler === "function") {
|
|
287
|
+
// eslint-disable-next-line prefer-rest-params
|
|
288
|
+
handler.apply(this, arguments);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
errMessage = "[NODE-OPCUA-W07] Unsupported Service : " + request.schema.name;
|
|
292
|
+
warningLog(errMessage);
|
|
293
|
+
debugLog(chalk.red.bold(errMessage));
|
|
294
|
+
response = makeServiceFault(node_opcua_status_code_1.StatusCodes.BadServiceUnsupported, [errMessage]);
|
|
295
|
+
channel.send_response("MSG", response, message, emptyCallback);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
/* istanbul ignore if */
|
|
300
|
+
const errMessage1 = "[NODE-OPCUA-W08] EXCEPTION CAUGHT WHILE PROCESSING REQUEST !! " + request.schema.name;
|
|
301
|
+
warningLog(chalk.red.bold(errMessage1));
|
|
302
|
+
warningLog(request.toString());
|
|
303
|
+
(0, node_opcua_debug_2.displayTraceFromThisProjectOnly)(err);
|
|
304
|
+
let additional_messages = [];
|
|
305
|
+
additional_messages.push("EXCEPTION CAUGHT WHILE PROCESSING REQUEST !!! " + request.schema.name);
|
|
306
|
+
if (err instanceof Error) {
|
|
307
|
+
additional_messages.push(err.message);
|
|
308
|
+
if (err.stack) {
|
|
309
|
+
additional_messages = additional_messages.concat(err.stack.split("\n"));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
response = makeServiceFault(node_opcua_status_code_1.StatusCodes.BadInternalError, additional_messages);
|
|
313
|
+
channel.send_response("MSG", response, message, emptyCallback);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* @private
|
|
318
|
+
*/
|
|
319
|
+
_get_endpoints(endpointUrl) {
|
|
320
|
+
let endpoints = [];
|
|
321
|
+
for (const endPoint of this.endpoints) {
|
|
322
|
+
const ep = endPoint.endpointDescriptions();
|
|
323
|
+
const epFiltered = endpointUrl ? ep.filter((e) => (0, node_opcua_utils_1.matchUri)(e.endpointUrl, endpointUrl)) : ep;
|
|
324
|
+
endpoints = endpoints.concat(epFiltered);
|
|
325
|
+
}
|
|
326
|
+
return endpoints;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* get one of the possible endpointUrl
|
|
330
|
+
*/
|
|
331
|
+
getEndpointUrl() {
|
|
332
|
+
return this._get_endpoints()[0].endpointUrl;
|
|
333
|
+
}
|
|
334
|
+
getDiscoveryUrls() {
|
|
335
|
+
const discoveryUrls = this.endpoints.map((e) => {
|
|
336
|
+
return e.endpointDescriptions()[0].endpointUrl;
|
|
337
|
+
});
|
|
338
|
+
return discoveryUrls;
|
|
339
|
+
}
|
|
340
|
+
getServers(channel) {
|
|
341
|
+
this.serverInfo.discoveryUrls = this.getDiscoveryUrls();
|
|
342
|
+
const servers = [this.serverInfo];
|
|
343
|
+
return servers;
|
|
344
|
+
}
|
|
345
|
+
suspendEndPoints(callback) {
|
|
346
|
+
/* istanbul ignore next */
|
|
347
|
+
if (!callback) {
|
|
348
|
+
throw new Error("Internal Error");
|
|
349
|
+
}
|
|
350
|
+
async.forEach(this.endpoints, (ep, _inner_callback) => {
|
|
351
|
+
/* istanbul ignore next */
|
|
352
|
+
if (doDebug) {
|
|
353
|
+
debugLog("Suspending ", ep.endpointDescriptions()[0].endpointUrl);
|
|
354
|
+
}
|
|
355
|
+
ep.suspendConnection((err) => {
|
|
356
|
+
/* istanbul ignore next */
|
|
357
|
+
if (doDebug) {
|
|
358
|
+
debugLog("Suspended ", ep.endpointDescriptions()[0].endpointUrl);
|
|
359
|
+
}
|
|
360
|
+
_inner_callback(err);
|
|
361
|
+
});
|
|
362
|
+
}, (err) => callback(err));
|
|
363
|
+
}
|
|
364
|
+
resumeEndPoints(callback) {
|
|
365
|
+
async.forEach(this.endpoints, (ep, _inner_callback) => {
|
|
366
|
+
ep.restoreConnection(_inner_callback);
|
|
367
|
+
}, (err) => callback(err));
|
|
368
|
+
}
|
|
369
|
+
prepare(message, channel) {
|
|
370
|
+
/* empty */
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* @private
|
|
374
|
+
*/
|
|
375
|
+
_on_GetEndpointsRequest(message, channel) {
|
|
376
|
+
const request = message.request;
|
|
377
|
+
(0, node_opcua_assert_1.assert)(request.schema.name === "GetEndpointsRequest");
|
|
378
|
+
const response = new node_opcua_service_endpoints_1.GetEndpointsResponse({});
|
|
379
|
+
response.endpoints = this._get_endpoints(null);
|
|
380
|
+
response.endpoints = response.endpoints.filter((endpoint) => !endpoint.restricted);
|
|
381
|
+
// apply filters
|
|
382
|
+
if (request.profileUris && request.profileUris.length > 0) {
|
|
383
|
+
response.endpoints = response.endpoints.filter((endpoint) => {
|
|
384
|
+
return request.profileUris.indexOf(endpoint.transportProfileUri) >= 0;
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
// adjust locale on ApplicationName to match requested local or provide
|
|
388
|
+
// a string with neutral locale (locale === null)
|
|
389
|
+
// TODO: find a better way to handle this
|
|
390
|
+
response.endpoints.forEach((endpoint) => {
|
|
391
|
+
endpoint.server.applicationName.locale = "en-US";
|
|
392
|
+
});
|
|
393
|
+
channel.send_response("MSG", response, message, emptyCallback);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* @private
|
|
397
|
+
*/
|
|
398
|
+
_on_FindServersRequest(message, channel) {
|
|
399
|
+
// Release 1.02 13 OPC Unified Architecture, Part 4 :
|
|
400
|
+
// This Service can be used without security and it is therefore vulnerable to Denial Of Service (DOS)
|
|
401
|
+
// attacks. A Server should minimize the amount of processing required to send the response for this
|
|
402
|
+
// Service. This can be achieved by preparing the result in advance. The Server should also add a
|
|
403
|
+
// short delay before starting processing of a request during high traffic conditions.
|
|
404
|
+
const shortDelay = 100; // milliseconds
|
|
405
|
+
setTimeout(() => {
|
|
406
|
+
const request = message.request;
|
|
407
|
+
(0, node_opcua_assert_1.assert)(request.schema.name === "FindServersRequest");
|
|
408
|
+
if (!(request instanceof node_opcua_service_discovery_1.FindServersRequest)) {
|
|
409
|
+
throw new Error("Invalid request type");
|
|
410
|
+
}
|
|
411
|
+
let servers = this.getServers(channel);
|
|
412
|
+
// apply filters
|
|
413
|
+
// TODO /
|
|
414
|
+
if (request.serverUris && request.serverUris.length > 0) {
|
|
415
|
+
// A serverUri matches the applicationUri from the ApplicationDescription define
|
|
416
|
+
servers = servers.filter((inner_Server) => {
|
|
417
|
+
return request.serverUris.indexOf(inner_Server.applicationUri) >= 0;
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
function adapt(applicationDescription) {
|
|
421
|
+
return new node_opcua_service_endpoints_2.ApplicationDescription({
|
|
422
|
+
applicationName: applicationDescription.applicationName,
|
|
423
|
+
applicationType: applicationDescription.applicationType,
|
|
424
|
+
applicationUri: applicationDescription.applicationUri,
|
|
425
|
+
discoveryProfileUri: applicationDescription.discoveryProfileUri,
|
|
426
|
+
discoveryUrls: applicationDescription.discoveryUrls,
|
|
427
|
+
gatewayServerUri: applicationDescription.gatewayServerUri,
|
|
428
|
+
productUri: applicationDescription.productUri
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
const response = new node_opcua_service_discovery_1.FindServersResponse({
|
|
432
|
+
servers: servers.map(adapt)
|
|
433
|
+
});
|
|
434
|
+
channel.send_response("MSG", response, message, emptyCallback);
|
|
435
|
+
}, shortDelay);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* returns a array of currently active channels
|
|
439
|
+
*/
|
|
440
|
+
getChannels() {
|
|
441
|
+
let channels = [];
|
|
442
|
+
for (const endpoint of this.endpoints) {
|
|
443
|
+
const c = endpoint.getChannels();
|
|
444
|
+
channels = channels.concat(c);
|
|
445
|
+
}
|
|
446
|
+
return channels;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
exports.OPCUABaseServer = OPCUABaseServer;
|
|
450
|
+
OPCUABaseServer.makeServiceFault = makeServiceFault;
|
|
451
|
+
/**
|
|
452
|
+
* construct a service Fault response
|
|
453
|
+
* @method makeServiceFault
|
|
454
|
+
* @param statusCode
|
|
455
|
+
* @param messages
|
|
456
|
+
*/
|
|
457
|
+
function makeServiceFault(statusCode, messages) {
|
|
458
|
+
const response = new node_opcua_service_secure_channel_1.ServiceFault();
|
|
459
|
+
response.responseHeader.serviceResult = statusCode;
|
|
460
|
+
// xx response.serviceDiagnostics.push( new DiagnosticInfo({ additionalInfo: messages.join("\n")}));
|
|
461
|
+
(0, node_opcua_assert_1.assert)(Array.isArray(messages));
|
|
462
|
+
(0, node_opcua_assert_1.assert)(typeof messages[0] === "string");
|
|
463
|
+
response.responseHeader.stringTable = messages;
|
|
464
|
+
// tslint:disable:no-console
|
|
465
|
+
warningLog(chalk.cyan(" messages "), messages.join("\n"));
|
|
466
|
+
return response;
|
|
467
|
+
}
|
|
468
|
+
// tslint:disable:no-var-requires
|
|
469
|
+
const thenify = require("thenify");
|
|
470
|
+
const opts = { multiArgs: false };
|
|
471
|
+
OPCUABaseServer.prototype.resumeEndPoints = thenify.withCallback(OPCUABaseServer.prototype.resumeEndPoints, opts);
|
|
472
|
+
OPCUABaseServer.prototype.suspendEndPoints = thenify.withCallback(OPCUABaseServer.prototype.suspendEndPoints, opts);
|
|
473
|
+
OPCUABaseServer.prototype.shutdownChannels = thenify.withCallback(OPCUABaseServer.prototype.shutdownChannels, opts);
|
|
474
474
|
//# sourceMappingURL=base_server.js.map
|