node-opcua-transport 2.76.0 → 2.76.2
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/source/AcknowledgeMessage.d.ts +27 -27
- package/dist/source/AcknowledgeMessage.js +78 -78
- package/dist/source/HelloMessage.d.ts +27 -27
- package/dist/source/HelloMessage.js +94 -94
- package/dist/source/TCPErrorMessage.d.ts +18 -18
- package/dist/source/TCPErrorMessage.js +46 -46
- package/dist/source/client_tcp_transport.d.ts +86 -86
- package/dist/source/client_tcp_transport.js +331 -331
- package/dist/source/index.d.ts +13 -13
- package/dist/source/index.js +29 -29
- package/dist/source/message_builder_base.d.ts +112 -112
- package/dist/source/message_builder_base.js +244 -244
- package/dist/source/server_tcp_transport.d.ts +44 -44
- package/dist/source/server_tcp_transport.js +228 -228
- package/dist/source/status_codes.d.ts +100 -100
- package/dist/source/status_codes.js +110 -110
- package/dist/source/tcp_transport.d.ts +136 -136
- package/dist/source/tcp_transport.js +376 -376
- package/dist/source/tools.d.ts +14 -14
- package/dist/source/tools.js +103 -103
- package/dist/source/utils.d.ts +3 -3
- package/dist/source/utils.js +9 -9
- package/dist/test-fixtures/fixture_full_tcp_packets.d.ts +21 -21
- package/dist/test-fixtures/fixture_full_tcp_packets.js +41 -41
- package/dist/test-fixtures/index.d.ts +1 -1
- package/dist/test-fixtures/index.js +17 -17
- package/dist/test_helpers/direct_transport.d.ts +18 -18
- package/dist/test_helpers/direct_transport.js +62 -62
- package/dist/test_helpers/fake_server.d.ts +19 -19
- package/dist/test_helpers/fake_server.js +54 -54
- package/dist/test_helpers/half_com_channel.d.ts +17 -17
- package/dist/test_helpers/half_com_channel.js +31 -31
- package/dist/test_helpers/index.d.ts +4 -4
- package/dist/test_helpers/index.js +20 -20
- package/dist/test_helpers/socket_transport.d.ts +10 -10
- package/dist/test_helpers/socket_transport.js +30 -30
- package/package.json +11 -11
|
@@ -1,332 +1,332 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ClientTCP_transport = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* @module node-opcua-transport
|
|
6
|
-
*/
|
|
7
|
-
const os = require("os");
|
|
8
|
-
const net_1 = require("net");
|
|
9
|
-
const chalk = require("chalk");
|
|
10
|
-
const node_opcua_assert_1 = require("node-opcua-assert");
|
|
11
|
-
const node_opcua_binary_stream_1 = require("node-opcua-binary-stream");
|
|
12
|
-
const node_opcua_chunkmanager_1 = require("node-opcua-chunkmanager");
|
|
13
|
-
const debug = require("node-opcua-debug");
|
|
14
|
-
const tcp_transport_1 = require("./tcp_transport");
|
|
15
|
-
const tools_1 = require("./tools");
|
|
16
|
-
const AcknowledgeMessage_1 = require("./AcknowledgeMessage");
|
|
17
|
-
const HelloMessage_1 = require("./HelloMessage");
|
|
18
|
-
const TCPErrorMessage_1 = require("./TCPErrorMessage");
|
|
19
|
-
const utils_1 = require("./utils");
|
|
20
|
-
const doDebug = debug.checkDebugFlag(__filename);
|
|
21
|
-
const debugLog = debug.make_debugLog(__filename);
|
|
22
|
-
const warningLog = debug.make_warningLog(__filename);
|
|
23
|
-
const errorLog = debug.make_errorLog(__filename);
|
|
24
|
-
const gHostname = os.hostname();
|
|
25
|
-
function createClientSocket(endpointUrl) {
|
|
26
|
-
// create a socket based on Url
|
|
27
|
-
const ep = (0, tools_1.parseEndpointUrl)(endpointUrl);
|
|
28
|
-
const port = parseInt(ep.port, 10);
|
|
29
|
-
const hostname = ep.hostname;
|
|
30
|
-
let socket;
|
|
31
|
-
switch (ep.protocol) {
|
|
32
|
-
case "opc.tcp:":
|
|
33
|
-
socket = (0, net_1.createConnection)({ host: hostname, port });
|
|
34
|
-
socket.setKeepAlive(true);
|
|
35
|
-
// Setting true for noDelay will immediately fire off data each time socket.write() is called.
|
|
36
|
-
socket.setNoDelay(true);
|
|
37
|
-
return socket;
|
|
38
|
-
case "fake:":
|
|
39
|
-
socket = (0, tcp_transport_1.getFakeTransport)();
|
|
40
|
-
(0, node_opcua_assert_1.assert)(ep.protocol === "fake:", " Unsupported transport protocol");
|
|
41
|
-
process.nextTick(() => socket.emit("connect"));
|
|
42
|
-
return socket;
|
|
43
|
-
case "websocket:":
|
|
44
|
-
case "http:":
|
|
45
|
-
case "https:FF":
|
|
46
|
-
default: {
|
|
47
|
-
const msg = "[NODE-OPCUA-E05] this transport protocol is not supported :" + ep.protocol;
|
|
48
|
-
errorLog(msg);
|
|
49
|
-
throw new Error(msg);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* a ClientTCP_transport connects to a remote server socket and
|
|
55
|
-
* initiates a communication with a HEL/ACK transaction.
|
|
56
|
-
* It negotiates the communication parameters with the other end.
|
|
57
|
-
*
|
|
58
|
-
* @class ClientTCP_transport
|
|
59
|
-
* @extends TCP_transport
|
|
60
|
-
* @constructor
|
|
61
|
-
* @example
|
|
62
|
-
*
|
|
63
|
-
* ```javascript
|
|
64
|
-
* const transport = ClientTCP_transport(url);
|
|
65
|
-
*
|
|
66
|
-
* transport.timeout = 10000;
|
|
67
|
-
*
|
|
68
|
-
* transport.connect(function(err)) {
|
|
69
|
-
* if (err) {
|
|
70
|
-
* // cannot connect
|
|
71
|
-
* } else {
|
|
72
|
-
* // connected
|
|
73
|
-
*
|
|
74
|
-
* }
|
|
75
|
-
* });
|
|
76
|
-
* ....
|
|
77
|
-
*
|
|
78
|
-
* transport.write(message_chunk,'F');
|
|
79
|
-
*
|
|
80
|
-
* ....
|
|
81
|
-
*
|
|
82
|
-
* transport.on("chunk",function(message_chunk) {
|
|
83
|
-
* // do something with chunk from server...
|
|
84
|
-
* });
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* ```
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*/
|
|
91
|
-
class ClientTCP_transport extends tcp_transport_1.TCP_transport {
|
|
92
|
-
constructor(transportSettings) {
|
|
93
|
-
super();
|
|
94
|
-
this.connected = false;
|
|
95
|
-
this.endpointUrl = "";
|
|
96
|
-
this.serverUri = "";
|
|
97
|
-
this._counter = 0;
|
|
98
|
-
this.numberOfRetry = 0;
|
|
99
|
-
// initially before HEL/ACK
|
|
100
|
-
this.maxChunkCount = 1;
|
|
101
|
-
this.maxMessageSize = 4 * 1024;
|
|
102
|
-
this.receiveBufferSize = 4 * 1024;
|
|
103
|
-
transportSettings = transportSettings || {};
|
|
104
|
-
this._helloSettings = {
|
|
105
|
-
maxChunkCount: transportSettings.maxChunkCount || ClientTCP_transport.defaultMaxChunk,
|
|
106
|
-
maxMessageSize: transportSettings.maxMessageSize || ClientTCP_transport.defaultMaxMessageSize,
|
|
107
|
-
receiveBufferSize: transportSettings.receiveBufferSize || ClientTCP_transport.defaultReceiveBufferSize,
|
|
108
|
-
sendBufferSize: transportSettings.sendBufferSize || ClientTCP_transport.defaultSendBufferSize
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
dispose() {
|
|
112
|
-
/* istanbul ignore next */
|
|
113
|
-
if (doDebug) {
|
|
114
|
-
debugLog(" ClientTCP_transport disposed");
|
|
115
|
-
}
|
|
116
|
-
super.dispose();
|
|
117
|
-
}
|
|
118
|
-
connect(endpointUrl, callback) {
|
|
119
|
-
(0, node_opcua_assert_1.assert)(arguments.length === 2);
|
|
120
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
121
|
-
const ep = (0, tools_1.parseEndpointUrl)(endpointUrl);
|
|
122
|
-
this.endpointUrl = endpointUrl;
|
|
123
|
-
this.serverUri = "urn:" + gHostname + ":Sample";
|
|
124
|
-
/* istanbul ignore next */
|
|
125
|
-
if (doDebug) {
|
|
126
|
-
debugLog(chalk.cyan("ClientTCP_transport#connect(endpointUrl = " + endpointUrl + ")"));
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
this._socket = createClientSocket(endpointUrl);
|
|
130
|
-
}
|
|
131
|
-
catch (err) {
|
|
132
|
-
/* istanbul ignore next */
|
|
133
|
-
if (doDebug) {
|
|
134
|
-
debugLog("CreateClientSocket has failed");
|
|
135
|
-
}
|
|
136
|
-
return callback(err);
|
|
137
|
-
}
|
|
138
|
-
const _on_socket_error_after_connection = (err) => {
|
|
139
|
-
/* istanbul ignore next */
|
|
140
|
-
if (doDebug) {
|
|
141
|
-
debugLog(" _on_socket_error_after_connection ClientTCP_transport Socket Error", err.message);
|
|
142
|
-
}
|
|
143
|
-
// EPIPE : EPIPE (Broken pipe): A write on a pipe, socket, or FIFO for which there is no process to read the
|
|
144
|
-
// data. Commonly encountered at the net and http layers, indicative that the remote side of the stream
|
|
145
|
-
// being written to has been closed.
|
|
146
|
-
// ECONNRESET (Connection reset by peer): A connection was forcibly closed by a peer. This normally results
|
|
147
|
-
// from a loss of the connection on the remote socket due to a timeout or reboot. Commonly encountered
|
|
148
|
-
// via the http and net module
|
|
149
|
-
if (err.message.match(/ECONNRESET|EPIPE/)) {
|
|
150
|
-
/**
|
|
151
|
-
* @event connection_break
|
|
152
|
-
*
|
|
153
|
-
*/
|
|
154
|
-
this.emit("connection_break");
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
const _on_socket_connect = () => {
|
|
158
|
-
/* istanbul ignore next */
|
|
159
|
-
if (doDebug) {
|
|
160
|
-
debugLog("entering _on_socket_connect");
|
|
161
|
-
}
|
|
162
|
-
_remove_connect_listeners();
|
|
163
|
-
this._perform_HEL_ACK_transaction((err) => {
|
|
164
|
-
if (!err) {
|
|
165
|
-
/* istanbul ignore next */
|
|
166
|
-
if (!this._socket) {
|
|
167
|
-
throw new Error("internal error");
|
|
168
|
-
}
|
|
169
|
-
// install error handler to detect connection break
|
|
170
|
-
this._socket.on("error", _on_socket_error_after_connection);
|
|
171
|
-
this.connected = true;
|
|
172
|
-
/**
|
|
173
|
-
* notify the observers that the transport is connected (the socket is connected and the the HEL/ACK
|
|
174
|
-
* transaction has been done)
|
|
175
|
-
* @event connect
|
|
176
|
-
*
|
|
177
|
-
*/
|
|
178
|
-
this.emit("connect");
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
debugLog("_perform_HEL_ACK_transaction has failed with err=", err.message);
|
|
182
|
-
}
|
|
183
|
-
callback(err);
|
|
184
|
-
});
|
|
185
|
-
};
|
|
186
|
-
const _on_socket_error_for_connect = (err) => {
|
|
187
|
-
// this handler will catch attempt to connect to an inaccessible address.
|
|
188
|
-
/* istanbul ignore next */
|
|
189
|
-
if (doDebug) {
|
|
190
|
-
debugLog(chalk.cyan("ClientTCP_transport#connect - _on_socket_error_for_connect"), err.message);
|
|
191
|
-
}
|
|
192
|
-
(0, node_opcua_assert_1.assert)(err instanceof Error);
|
|
193
|
-
_remove_connect_listeners();
|
|
194
|
-
callback(err);
|
|
195
|
-
};
|
|
196
|
-
const _on_socket_end_for_connect = (err) => {
|
|
197
|
-
/* istanbul ignore next */
|
|
198
|
-
if (doDebug) {
|
|
199
|
-
debugLog(chalk.cyan("ClientTCP_transport#connect -> _on_socket_end_for_connect Socket has been closed by server"), err);
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
const _remove_connect_listeners = () => {
|
|
203
|
-
/* istanbul ignore next */
|
|
204
|
-
if (!this._socket) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
this._socket.removeListener("error", _on_socket_error_for_connect);
|
|
208
|
-
this._socket.removeListener("end", _on_socket_end_for_connect);
|
|
209
|
-
};
|
|
210
|
-
this._socket.once("error", _on_socket_error_for_connect);
|
|
211
|
-
this._socket.once("end", _on_socket_end_for_connect);
|
|
212
|
-
this._socket.once("connect", _on_socket_connect);
|
|
213
|
-
this._install_socket(this._socket);
|
|
214
|
-
}
|
|
215
|
-
on_socket_ended(err) {
|
|
216
|
-
debugLog("on_socket_ended", this.name, err ? err.message : "");
|
|
217
|
-
if (this.connected) {
|
|
218
|
-
super.on_socket_ended(err);
|
|
219
|
-
}
|
|
220
|
-
// if (this._socket) {
|
|
221
|
-
// this._socket.removeAllListeners();
|
|
222
|
-
// }
|
|
223
|
-
}
|
|
224
|
-
_handle_ACK_response(messageChunk, callback) {
|
|
225
|
-
const _stream = new node_opcua_binary_stream_1.BinaryStream(messageChunk);
|
|
226
|
-
const messageHeader = (0, node_opcua_chunkmanager_1.readMessageHeader)(_stream);
|
|
227
|
-
let err;
|
|
228
|
-
/* istanbul ignore next */
|
|
229
|
-
if (messageHeader.isFinal !== "F") {
|
|
230
|
-
err = new Error(" invalid ACK message");
|
|
231
|
-
return callback(err);
|
|
232
|
-
}
|
|
233
|
-
let responseClass;
|
|
234
|
-
let response;
|
|
235
|
-
if (messageHeader.msgType === "ERR") {
|
|
236
|
-
responseClass = TCPErrorMessage_1.TCPErrorMessage;
|
|
237
|
-
_stream.rewind();
|
|
238
|
-
response = (0, tools_1.decodeMessage)(_stream, responseClass);
|
|
239
|
-
err = new Error("ACK: ERR received " + response.statusCode.toString() + " : " + response.reason);
|
|
240
|
-
err.statusCode = response.statusCode;
|
|
241
|
-
// istanbul ignore next
|
|
242
|
-
if (utils_1.doTraceHelloAck) {
|
|
243
|
-
warningLog("receiving ERR instead of Ack", response.toString());
|
|
244
|
-
}
|
|
245
|
-
callback(err);
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
responseClass = AcknowledgeMessage_1.AcknowledgeMessage;
|
|
249
|
-
_stream.rewind();
|
|
250
|
-
response = (0, tools_1.decodeMessage)(_stream, responseClass);
|
|
251
|
-
this.parameters = response;
|
|
252
|
-
this.setLimits(response);
|
|
253
|
-
// istanbul ignore next
|
|
254
|
-
if (utils_1.doTraceHelloAck) {
|
|
255
|
-
warningLog("receiving Ack\n", response.toString());
|
|
256
|
-
}
|
|
257
|
-
callback();
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
_send_HELLO_request() {
|
|
261
|
-
/* istanbul ignore next */
|
|
262
|
-
if (doDebug) {
|
|
263
|
-
debugLog("entering _send_HELLO_request");
|
|
264
|
-
}
|
|
265
|
-
(0, node_opcua_assert_1.assert)(this._socket);
|
|
266
|
-
(0, node_opcua_assert_1.assert)(isFinite(this.protocolVersion));
|
|
267
|
-
(0, node_opcua_assert_1.assert)(this.endpointUrl.length > 0, " expecting a valid endpoint url");
|
|
268
|
-
const { maxChunkCount, maxMessageSize, receiveBufferSize, sendBufferSize } = this._helloSettings;
|
|
269
|
-
// Write a message to the socket as soon as the client is connected,
|
|
270
|
-
// the server will receive it as message from the client
|
|
271
|
-
const helloMessage = new HelloMessage_1.HelloMessage({
|
|
272
|
-
endpointUrl: this.endpointUrl,
|
|
273
|
-
protocolVersion: this.protocolVersion,
|
|
274
|
-
maxChunkCount,
|
|
275
|
-
maxMessageSize,
|
|
276
|
-
receiveBufferSize,
|
|
277
|
-
sendBufferSize
|
|
278
|
-
});
|
|
279
|
-
// istanbul ignore next
|
|
280
|
-
if (utils_1.doTraceHelloAck) {
|
|
281
|
-
warningLog(`sending Hello\n ${helloMessage.toString()}`);
|
|
282
|
-
}
|
|
283
|
-
const messageChunk = (0, tools_1.packTcpMessage)("HEL", helloMessage);
|
|
284
|
-
this._write_chunk(messageChunk);
|
|
285
|
-
}
|
|
286
|
-
_on_ACK_response(externalCallback, err, data) {
|
|
287
|
-
/* istanbul ignore next */
|
|
288
|
-
if (doDebug) {
|
|
289
|
-
debugLog("entering _on_ACK_response");
|
|
290
|
-
}
|
|
291
|
-
(0, node_opcua_assert_1.assert)(typeof externalCallback === "function");
|
|
292
|
-
(0, node_opcua_assert_1.assert)(this._counter === 0, "Ack response should only be received once !");
|
|
293
|
-
this._counter += 1;
|
|
294
|
-
if (err || !data) {
|
|
295
|
-
externalCallback(err || new Error("no data"));
|
|
296
|
-
if (this._socket) {
|
|
297
|
-
this._socket.end();
|
|
298
|
-
// Xx this._socket.removeAllListeners();
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
this._handle_ACK_response(data, externalCallback);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
_perform_HEL_ACK_transaction(callback) {
|
|
306
|
-
/* istanbul ignore next */
|
|
307
|
-
if (!this._socket) {
|
|
308
|
-
return callback(new Error("No socket available to perform HEL/ACK transaction"));
|
|
309
|
-
}
|
|
310
|
-
(0, node_opcua_assert_1.assert)(this._socket, "expecting a valid socket to send a message");
|
|
311
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
312
|
-
this._counter = 0;
|
|
313
|
-
/* istanbul ignore next */
|
|
314
|
-
if (doDebug) {
|
|
315
|
-
debugLog("entering _perform_HEL_ACK_transaction");
|
|
316
|
-
}
|
|
317
|
-
this._install_one_time_message_receiver((err, data) => {
|
|
318
|
-
/* istanbul ignore next */
|
|
319
|
-
if (doDebug) {
|
|
320
|
-
debugLog("before _on_ACK_response ", err ? err.message : "");
|
|
321
|
-
}
|
|
322
|
-
this._on_ACK_response(callback, err, data);
|
|
323
|
-
});
|
|
324
|
-
this._send_HELLO_request();
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
exports.ClientTCP_transport = ClientTCP_transport;
|
|
328
|
-
ClientTCP_transport.defaultMaxChunk = 0; // 0 - no limits
|
|
329
|
-
ClientTCP_transport.defaultMaxMessageSize = 0; // 0 - no limits
|
|
330
|
-
ClientTCP_transport.defaultReceiveBufferSize = 1024 * 64 * 10;
|
|
331
|
-
ClientTCP_transport.defaultSendBufferSize = 1024 * 64 * 10; // 8192 min,
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClientTCP_transport = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @module node-opcua-transport
|
|
6
|
+
*/
|
|
7
|
+
const os = require("os");
|
|
8
|
+
const net_1 = require("net");
|
|
9
|
+
const chalk = require("chalk");
|
|
10
|
+
const node_opcua_assert_1 = require("node-opcua-assert");
|
|
11
|
+
const node_opcua_binary_stream_1 = require("node-opcua-binary-stream");
|
|
12
|
+
const node_opcua_chunkmanager_1 = require("node-opcua-chunkmanager");
|
|
13
|
+
const debug = require("node-opcua-debug");
|
|
14
|
+
const tcp_transport_1 = require("./tcp_transport");
|
|
15
|
+
const tools_1 = require("./tools");
|
|
16
|
+
const AcknowledgeMessage_1 = require("./AcknowledgeMessage");
|
|
17
|
+
const HelloMessage_1 = require("./HelloMessage");
|
|
18
|
+
const TCPErrorMessage_1 = require("./TCPErrorMessage");
|
|
19
|
+
const utils_1 = require("./utils");
|
|
20
|
+
const doDebug = debug.checkDebugFlag(__filename);
|
|
21
|
+
const debugLog = debug.make_debugLog(__filename);
|
|
22
|
+
const warningLog = debug.make_warningLog(__filename);
|
|
23
|
+
const errorLog = debug.make_errorLog(__filename);
|
|
24
|
+
const gHostname = os.hostname();
|
|
25
|
+
function createClientSocket(endpointUrl) {
|
|
26
|
+
// create a socket based on Url
|
|
27
|
+
const ep = (0, tools_1.parseEndpointUrl)(endpointUrl);
|
|
28
|
+
const port = parseInt(ep.port, 10);
|
|
29
|
+
const hostname = ep.hostname;
|
|
30
|
+
let socket;
|
|
31
|
+
switch (ep.protocol) {
|
|
32
|
+
case "opc.tcp:":
|
|
33
|
+
socket = (0, net_1.createConnection)({ host: hostname, port });
|
|
34
|
+
socket.setKeepAlive(true);
|
|
35
|
+
// Setting true for noDelay will immediately fire off data each time socket.write() is called.
|
|
36
|
+
socket.setNoDelay(true);
|
|
37
|
+
return socket;
|
|
38
|
+
case "fake:":
|
|
39
|
+
socket = (0, tcp_transport_1.getFakeTransport)();
|
|
40
|
+
(0, node_opcua_assert_1.assert)(ep.protocol === "fake:", " Unsupported transport protocol");
|
|
41
|
+
process.nextTick(() => socket.emit("connect"));
|
|
42
|
+
return socket;
|
|
43
|
+
case "websocket:":
|
|
44
|
+
case "http:":
|
|
45
|
+
case "https:FF":
|
|
46
|
+
default: {
|
|
47
|
+
const msg = "[NODE-OPCUA-E05] this transport protocol is not supported :" + ep.protocol;
|
|
48
|
+
errorLog(msg);
|
|
49
|
+
throw new Error(msg);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* a ClientTCP_transport connects to a remote server socket and
|
|
55
|
+
* initiates a communication with a HEL/ACK transaction.
|
|
56
|
+
* It negotiates the communication parameters with the other end.
|
|
57
|
+
*
|
|
58
|
+
* @class ClientTCP_transport
|
|
59
|
+
* @extends TCP_transport
|
|
60
|
+
* @constructor
|
|
61
|
+
* @example
|
|
62
|
+
*
|
|
63
|
+
* ```javascript
|
|
64
|
+
* const transport = ClientTCP_transport(url);
|
|
65
|
+
*
|
|
66
|
+
* transport.timeout = 10000;
|
|
67
|
+
*
|
|
68
|
+
* transport.connect(function(err)) {
|
|
69
|
+
* if (err) {
|
|
70
|
+
* // cannot connect
|
|
71
|
+
* } else {
|
|
72
|
+
* // connected
|
|
73
|
+
*
|
|
74
|
+
* }
|
|
75
|
+
* });
|
|
76
|
+
* ....
|
|
77
|
+
*
|
|
78
|
+
* transport.write(message_chunk,'F');
|
|
79
|
+
*
|
|
80
|
+
* ....
|
|
81
|
+
*
|
|
82
|
+
* transport.on("chunk",function(message_chunk) {
|
|
83
|
+
* // do something with chunk from server...
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
*
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
*
|
|
90
|
+
*/
|
|
91
|
+
class ClientTCP_transport extends tcp_transport_1.TCP_transport {
|
|
92
|
+
constructor(transportSettings) {
|
|
93
|
+
super();
|
|
94
|
+
this.connected = false;
|
|
95
|
+
this.endpointUrl = "";
|
|
96
|
+
this.serverUri = "";
|
|
97
|
+
this._counter = 0;
|
|
98
|
+
this.numberOfRetry = 0;
|
|
99
|
+
// initially before HEL/ACK
|
|
100
|
+
this.maxChunkCount = 1;
|
|
101
|
+
this.maxMessageSize = 4 * 1024;
|
|
102
|
+
this.receiveBufferSize = 4 * 1024;
|
|
103
|
+
transportSettings = transportSettings || {};
|
|
104
|
+
this._helloSettings = {
|
|
105
|
+
maxChunkCount: transportSettings.maxChunkCount || ClientTCP_transport.defaultMaxChunk,
|
|
106
|
+
maxMessageSize: transportSettings.maxMessageSize || ClientTCP_transport.defaultMaxMessageSize,
|
|
107
|
+
receiveBufferSize: transportSettings.receiveBufferSize || ClientTCP_transport.defaultReceiveBufferSize,
|
|
108
|
+
sendBufferSize: transportSettings.sendBufferSize || ClientTCP_transport.defaultSendBufferSize
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
dispose() {
|
|
112
|
+
/* istanbul ignore next */
|
|
113
|
+
if (doDebug) {
|
|
114
|
+
debugLog(" ClientTCP_transport disposed");
|
|
115
|
+
}
|
|
116
|
+
super.dispose();
|
|
117
|
+
}
|
|
118
|
+
connect(endpointUrl, callback) {
|
|
119
|
+
(0, node_opcua_assert_1.assert)(arguments.length === 2);
|
|
120
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
121
|
+
const ep = (0, tools_1.parseEndpointUrl)(endpointUrl);
|
|
122
|
+
this.endpointUrl = endpointUrl;
|
|
123
|
+
this.serverUri = "urn:" + gHostname + ":Sample";
|
|
124
|
+
/* istanbul ignore next */
|
|
125
|
+
if (doDebug) {
|
|
126
|
+
debugLog(chalk.cyan("ClientTCP_transport#connect(endpointUrl = " + endpointUrl + ")"));
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
this._socket = createClientSocket(endpointUrl);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
/* istanbul ignore next */
|
|
133
|
+
if (doDebug) {
|
|
134
|
+
debugLog("CreateClientSocket has failed");
|
|
135
|
+
}
|
|
136
|
+
return callback(err);
|
|
137
|
+
}
|
|
138
|
+
const _on_socket_error_after_connection = (err) => {
|
|
139
|
+
/* istanbul ignore next */
|
|
140
|
+
if (doDebug) {
|
|
141
|
+
debugLog(" _on_socket_error_after_connection ClientTCP_transport Socket Error", err.message);
|
|
142
|
+
}
|
|
143
|
+
// EPIPE : EPIPE (Broken pipe): A write on a pipe, socket, or FIFO for which there is no process to read the
|
|
144
|
+
// data. Commonly encountered at the net and http layers, indicative that the remote side of the stream
|
|
145
|
+
// being written to has been closed.
|
|
146
|
+
// ECONNRESET (Connection reset by peer): A connection was forcibly closed by a peer. This normally results
|
|
147
|
+
// from a loss of the connection on the remote socket due to a timeout or reboot. Commonly encountered
|
|
148
|
+
// via the http and net module
|
|
149
|
+
if (err.message.match(/ECONNRESET|EPIPE/)) {
|
|
150
|
+
/**
|
|
151
|
+
* @event connection_break
|
|
152
|
+
*
|
|
153
|
+
*/
|
|
154
|
+
this.emit("connection_break");
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const _on_socket_connect = () => {
|
|
158
|
+
/* istanbul ignore next */
|
|
159
|
+
if (doDebug) {
|
|
160
|
+
debugLog("entering _on_socket_connect");
|
|
161
|
+
}
|
|
162
|
+
_remove_connect_listeners();
|
|
163
|
+
this._perform_HEL_ACK_transaction((err) => {
|
|
164
|
+
if (!err) {
|
|
165
|
+
/* istanbul ignore next */
|
|
166
|
+
if (!this._socket) {
|
|
167
|
+
throw new Error("internal error");
|
|
168
|
+
}
|
|
169
|
+
// install error handler to detect connection break
|
|
170
|
+
this._socket.on("error", _on_socket_error_after_connection);
|
|
171
|
+
this.connected = true;
|
|
172
|
+
/**
|
|
173
|
+
* notify the observers that the transport is connected (the socket is connected and the the HEL/ACK
|
|
174
|
+
* transaction has been done)
|
|
175
|
+
* @event connect
|
|
176
|
+
*
|
|
177
|
+
*/
|
|
178
|
+
this.emit("connect");
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
debugLog("_perform_HEL_ACK_transaction has failed with err=", err.message);
|
|
182
|
+
}
|
|
183
|
+
callback(err);
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
const _on_socket_error_for_connect = (err) => {
|
|
187
|
+
// this handler will catch attempt to connect to an inaccessible address.
|
|
188
|
+
/* istanbul ignore next */
|
|
189
|
+
if (doDebug) {
|
|
190
|
+
debugLog(chalk.cyan("ClientTCP_transport#connect - _on_socket_error_for_connect"), err.message);
|
|
191
|
+
}
|
|
192
|
+
(0, node_opcua_assert_1.assert)(err instanceof Error);
|
|
193
|
+
_remove_connect_listeners();
|
|
194
|
+
callback(err);
|
|
195
|
+
};
|
|
196
|
+
const _on_socket_end_for_connect = (err) => {
|
|
197
|
+
/* istanbul ignore next */
|
|
198
|
+
if (doDebug) {
|
|
199
|
+
debugLog(chalk.cyan("ClientTCP_transport#connect -> _on_socket_end_for_connect Socket has been closed by server"), err);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
const _remove_connect_listeners = () => {
|
|
203
|
+
/* istanbul ignore next */
|
|
204
|
+
if (!this._socket) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this._socket.removeListener("error", _on_socket_error_for_connect);
|
|
208
|
+
this._socket.removeListener("end", _on_socket_end_for_connect);
|
|
209
|
+
};
|
|
210
|
+
this._socket.once("error", _on_socket_error_for_connect);
|
|
211
|
+
this._socket.once("end", _on_socket_end_for_connect);
|
|
212
|
+
this._socket.once("connect", _on_socket_connect);
|
|
213
|
+
this._install_socket(this._socket);
|
|
214
|
+
}
|
|
215
|
+
on_socket_ended(err) {
|
|
216
|
+
debugLog("on_socket_ended", this.name, err ? err.message : "");
|
|
217
|
+
if (this.connected) {
|
|
218
|
+
super.on_socket_ended(err);
|
|
219
|
+
}
|
|
220
|
+
// if (this._socket) {
|
|
221
|
+
// this._socket.removeAllListeners();
|
|
222
|
+
// }
|
|
223
|
+
}
|
|
224
|
+
_handle_ACK_response(messageChunk, callback) {
|
|
225
|
+
const _stream = new node_opcua_binary_stream_1.BinaryStream(messageChunk);
|
|
226
|
+
const messageHeader = (0, node_opcua_chunkmanager_1.readMessageHeader)(_stream);
|
|
227
|
+
let err;
|
|
228
|
+
/* istanbul ignore next */
|
|
229
|
+
if (messageHeader.isFinal !== "F") {
|
|
230
|
+
err = new Error(" invalid ACK message");
|
|
231
|
+
return callback(err);
|
|
232
|
+
}
|
|
233
|
+
let responseClass;
|
|
234
|
+
let response;
|
|
235
|
+
if (messageHeader.msgType === "ERR") {
|
|
236
|
+
responseClass = TCPErrorMessage_1.TCPErrorMessage;
|
|
237
|
+
_stream.rewind();
|
|
238
|
+
response = (0, tools_1.decodeMessage)(_stream, responseClass);
|
|
239
|
+
err = new Error("ACK: ERR received " + response.statusCode.toString() + " : " + response.reason);
|
|
240
|
+
err.statusCode = response.statusCode;
|
|
241
|
+
// istanbul ignore next
|
|
242
|
+
if (utils_1.doTraceHelloAck) {
|
|
243
|
+
warningLog("receiving ERR instead of Ack", response.toString());
|
|
244
|
+
}
|
|
245
|
+
callback(err);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
responseClass = AcknowledgeMessage_1.AcknowledgeMessage;
|
|
249
|
+
_stream.rewind();
|
|
250
|
+
response = (0, tools_1.decodeMessage)(_stream, responseClass);
|
|
251
|
+
this.parameters = response;
|
|
252
|
+
this.setLimits(response);
|
|
253
|
+
// istanbul ignore next
|
|
254
|
+
if (utils_1.doTraceHelloAck) {
|
|
255
|
+
warningLog("receiving Ack\n", response.toString());
|
|
256
|
+
}
|
|
257
|
+
callback();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
_send_HELLO_request() {
|
|
261
|
+
/* istanbul ignore next */
|
|
262
|
+
if (doDebug) {
|
|
263
|
+
debugLog("entering _send_HELLO_request");
|
|
264
|
+
}
|
|
265
|
+
(0, node_opcua_assert_1.assert)(this._socket);
|
|
266
|
+
(0, node_opcua_assert_1.assert)(isFinite(this.protocolVersion));
|
|
267
|
+
(0, node_opcua_assert_1.assert)(this.endpointUrl.length > 0, " expecting a valid endpoint url");
|
|
268
|
+
const { maxChunkCount, maxMessageSize, receiveBufferSize, sendBufferSize } = this._helloSettings;
|
|
269
|
+
// Write a message to the socket as soon as the client is connected,
|
|
270
|
+
// the server will receive it as message from the client
|
|
271
|
+
const helloMessage = new HelloMessage_1.HelloMessage({
|
|
272
|
+
endpointUrl: this.endpointUrl,
|
|
273
|
+
protocolVersion: this.protocolVersion,
|
|
274
|
+
maxChunkCount,
|
|
275
|
+
maxMessageSize,
|
|
276
|
+
receiveBufferSize,
|
|
277
|
+
sendBufferSize
|
|
278
|
+
});
|
|
279
|
+
// istanbul ignore next
|
|
280
|
+
if (utils_1.doTraceHelloAck) {
|
|
281
|
+
warningLog(`sending Hello\n ${helloMessage.toString()}`);
|
|
282
|
+
}
|
|
283
|
+
const messageChunk = (0, tools_1.packTcpMessage)("HEL", helloMessage);
|
|
284
|
+
this._write_chunk(messageChunk);
|
|
285
|
+
}
|
|
286
|
+
_on_ACK_response(externalCallback, err, data) {
|
|
287
|
+
/* istanbul ignore next */
|
|
288
|
+
if (doDebug) {
|
|
289
|
+
debugLog("entering _on_ACK_response");
|
|
290
|
+
}
|
|
291
|
+
(0, node_opcua_assert_1.assert)(typeof externalCallback === "function");
|
|
292
|
+
(0, node_opcua_assert_1.assert)(this._counter === 0, "Ack response should only be received once !");
|
|
293
|
+
this._counter += 1;
|
|
294
|
+
if (err || !data) {
|
|
295
|
+
externalCallback(err || new Error("no data"));
|
|
296
|
+
if (this._socket) {
|
|
297
|
+
this._socket.end();
|
|
298
|
+
// Xx this._socket.removeAllListeners();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
this._handle_ACK_response(data, externalCallback);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
_perform_HEL_ACK_transaction(callback) {
|
|
306
|
+
/* istanbul ignore next */
|
|
307
|
+
if (!this._socket) {
|
|
308
|
+
return callback(new Error("No socket available to perform HEL/ACK transaction"));
|
|
309
|
+
}
|
|
310
|
+
(0, node_opcua_assert_1.assert)(this._socket, "expecting a valid socket to send a message");
|
|
311
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
312
|
+
this._counter = 0;
|
|
313
|
+
/* istanbul ignore next */
|
|
314
|
+
if (doDebug) {
|
|
315
|
+
debugLog("entering _perform_HEL_ACK_transaction");
|
|
316
|
+
}
|
|
317
|
+
this._install_one_time_message_receiver((err, data) => {
|
|
318
|
+
/* istanbul ignore next */
|
|
319
|
+
if (doDebug) {
|
|
320
|
+
debugLog("before _on_ACK_response ", err ? err.message : "");
|
|
321
|
+
}
|
|
322
|
+
this._on_ACK_response(callback, err, data);
|
|
323
|
+
});
|
|
324
|
+
this._send_HELLO_request();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
exports.ClientTCP_transport = ClientTCP_transport;
|
|
328
|
+
ClientTCP_transport.defaultMaxChunk = 0; // 0 - no limits
|
|
329
|
+
ClientTCP_transport.defaultMaxMessageSize = 0; // 0 - no limits
|
|
330
|
+
ClientTCP_transport.defaultReceiveBufferSize = 1024 * 64 * 10;
|
|
331
|
+
ClientTCP_transport.defaultSendBufferSize = 1024 * 64 * 10; // 8192 min,
|
|
332
332
|
//# sourceMappingURL=client_tcp_transport.js.map
|