node-opcua-transport 2.68.0 → 2.70.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/source/client_tcp_transport.d.ts +8 -4
- package/dist/source/client_tcp_transport.js +15 -6
- package/dist/source/client_tcp_transport.js.map +1 -1
- package/dist/source/index.d.ts +5 -3
- package/dist/source/index.js +5 -3
- package/dist/source/index.js.map +1 -1
- package/dist/source/message_builder_base.d.ts +27 -7
- package/dist/source/message_builder_base.js +36 -13
- package/dist/source/message_builder_base.js.map +1 -1
- package/dist/source/server_tcp_transport.d.ts +1 -5
- package/dist/source/server_tcp_transport.js +41 -39
- package/dist/source/server_tcp_transport.js.map +1 -1
- package/dist/source/status_codes.d.ts +100 -0
- package/dist/source/status_codes.js +111 -0
- package/dist/source/status_codes.js.map +1 -0
- package/dist/source/tcp_transport.d.ts +21 -28
- package/dist/source/tcp_transport.js +86 -57
- package/dist/source/tcp_transport.js.map +1 -1
- package/dist/source/tools.d.ts +1 -0
- package/dist/source/utils.d.ts +3 -2
- package/dist/source/utils.js +4 -3
- package/dist/source/utils.js.map +1 -1
- package/dist/test_helpers/direct_transport.d.ts +4 -0
- package/dist/test_helpers/direct_transport.js.map +1 -1
- package/dist/test_helpers/fake_server.d.ts +2 -0
- package/dist/test_helpers/half_com_channel.d.ts +7 -0
- package/dist/test_helpers/half_com_channel.js.map +1 -1
- package/package.json +16 -17
- package/source/client_tcp_transport.ts +21 -9
- package/source/index.ts +5 -3
- package/source/message_builder_base.ts +68 -16
- package/source/server_tcp_transport.ts +50 -47
- package/source/status_codes.ts +109 -0
- package/source/tcp_transport.ts +118 -73
- package/source/utils.ts +3 -2
- package/test_helpers/direct_transport.ts +4 -1
- package/test_helpers/half_com_channel.ts +7 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { EventEmitter } from "events";
|
|
3
4
|
import { HalfComChannel } from "./half_com_channel";
|
|
5
|
+
export interface DirectTransport {
|
|
6
|
+
on(eventName: "end", eventHandler: () => void): this;
|
|
7
|
+
}
|
|
4
8
|
export declare class DirectTransport extends EventEmitter {
|
|
5
9
|
client: HalfComChannel;
|
|
6
10
|
server: HalfComChannel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"direct_transport.js","sourceRoot":"","sources":["../../test_helpers/direct_transport.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AACtC,yDAA2C;AAC3C,sCAA6C;AAC7C,yDAAoD;
|
|
1
|
+
{"version":3,"file":"direct_transport.js","sourceRoot":"","sources":["../../test_helpers/direct_transport.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AACtC,yDAA2C;AAC3C,sCAA6C;AAC7C,yDAAoD;AAKpD,MAAa,eAAgB,SAAQ,qBAAY;IAM7C;QACI,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,MAAM,GAAG,IAAI,iCAAc,EAAE,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,iCAAc,EAAE,CAAC;QAEnC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE;YACjC,IAAA,0BAAM,EAAC,IAAI,YAAY,MAAM,CAAC,CAAC;YAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE;YACjC,IAAA,0BAAM,EAAC,IAAI,YAAY,MAAM,CAAC,CAAC;YAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC1B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;QACjC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC1B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE;YAClC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACpC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YAChC,IAAI,IAAI,EAAE;gBACN,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;aAC3B;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,GAAG,mCAAmC,CAAC;IACnD,CAAC;IAEM,UAAU,CAAC,IAAgB;QAC9B,IAAA,yBAAgB,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,EAAE,CAAC;IACX,CAAC;IAEM,QAAQ,CAAC,IAAgB;QAC5B,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QAClB,IAAI,IAAI,EAAE;YACN,YAAY,CAAC,IAAI,CAAC,CAAC;SACtB;IACL,CAAC;IAEM,WAAW;QACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;YAClB,OAAO,IAAI,CAAC;SACf;QACD,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACnC,CAAC;IAEM,YAAY,CAAC,IAAoD;QACpE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;CACJ;AAnED,0CAmEC"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { EventEmitter } from "events";
|
|
4
|
+
export interface HalfComChannel {
|
|
5
|
+
on(eventName: "data", eventHandler: (data: Buffer) => void): this;
|
|
6
|
+
on(eventName: "send_data", eventHandler: (data: Buffer) => void): this;
|
|
7
|
+
on(eventName: "ending", eventHandler: () => void): this;
|
|
8
|
+
on(eventName: "end", eventHandler: (err?: Error) => void): this;
|
|
9
|
+
}
|
|
3
10
|
export declare class HalfComChannel extends EventEmitter {
|
|
4
11
|
_hasEnded: boolean;
|
|
5
12
|
constructor();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"half_com_channel.js","sourceRoot":"","sources":["../../test_helpers/half_com_channel.ts"],"names":[],"mappings":";;;AAAA,yDAAyD;AACzD,mCAAsC;AACtC,yDAA2C;
|
|
1
|
+
{"version":3,"file":"half_com_channel.js","sourceRoot":"","sources":["../../test_helpers/half_com_channel.ts"],"names":[],"mappings":";;;AAAA,yDAAyD;AACzD,mCAAsC;AACtC,yDAA2C;AAS3C,MAAa,cAAe,SAAQ,qBAAY;IAG5C;QACI,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IAC3B,CAAC;IAEM,KAAK,CAAC,IAAqB;QAC9B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;YAC1B,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SAC5B;QACD,IAAA,0BAAM,EAAC,IAAI,YAAY,MAAM,EAAE,yCAAyC,CAAC,CAAC;QAC1E,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAEM,GAAG;QACN,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACjB,IAAA,0BAAM,EAAC,CAAC,IAAI,CAAC,SAAS,EAAE,gDAAgD,CAAC,CAAC;YAC1E,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SACpB;IACL,CAAC;IAEM,OAAO,KAAU,CAAC;IAElB,UAAU,KAAU,CAAC;CAC/B;AA7BD,wCA6BC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-opcua-transport",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.70.0",
|
|
4
4
|
"description": "pure nodejs OPCUA SDK - module -transport",
|
|
5
5
|
"main": "./dist/source/index.js",
|
|
6
6
|
"types": "./dist/source/index.d.ts",
|
|
@@ -14,24 +14,23 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"chalk": "4.1.2",
|
|
16
16
|
"node-opcua-assert": "2.66.0",
|
|
17
|
-
"node-opcua-basic-types": "2.
|
|
18
|
-
"node-opcua-binary-stream": "2.
|
|
19
|
-
"node-opcua-buffer-utils": "2.
|
|
20
|
-
"node-opcua-chunkmanager": "2.
|
|
21
|
-
"node-opcua-debug": "2.
|
|
22
|
-
"node-opcua-factory": "2.
|
|
23
|
-
"node-opcua-nodeid": "2.
|
|
24
|
-
"node-opcua-object-registry": "2.
|
|
25
|
-
"node-opcua-packet-assembler": "2.
|
|
26
|
-
"node-opcua-status-code": "2.
|
|
27
|
-
"node-opcua-types": "2.
|
|
28
|
-
"node-opcua-utils": "2.
|
|
17
|
+
"node-opcua-basic-types": "2.70.0",
|
|
18
|
+
"node-opcua-binary-stream": "2.69.0",
|
|
19
|
+
"node-opcua-buffer-utils": "2.69.0",
|
|
20
|
+
"node-opcua-chunkmanager": "2.70.0",
|
|
21
|
+
"node-opcua-debug": "2.69.0",
|
|
22
|
+
"node-opcua-factory": "2.70.0",
|
|
23
|
+
"node-opcua-nodeid": "2.70.0",
|
|
24
|
+
"node-opcua-object-registry": "2.69.0",
|
|
25
|
+
"node-opcua-packet-assembler": "2.69.0",
|
|
26
|
+
"node-opcua-status-code": "2.70.0",
|
|
27
|
+
"node-opcua-types": "2.70.0",
|
|
28
|
+
"node-opcua-utils": "2.69.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@types/node": "17.0.
|
|
32
|
-
"node-opcua-debug": "2.66.0",
|
|
31
|
+
"@types/node": "17.0.36",
|
|
33
32
|
"should": "^13.2.3",
|
|
34
|
-
"sinon": "^
|
|
33
|
+
"sinon": "^14.0.0"
|
|
35
34
|
},
|
|
36
35
|
"author": "Etienne Rossignon",
|
|
37
36
|
"license": "MIT",
|
|
@@ -48,5 +47,5 @@
|
|
|
48
47
|
"internet of things"
|
|
49
48
|
],
|
|
50
49
|
"homepage": "http://node-opcua.github.io/",
|
|
51
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "3b692ffb3e2ef0542da32f349fb0c76b3acc75ce"
|
|
52
51
|
}
|
|
@@ -56,12 +56,12 @@ function createClientSocket(endpointUrl: string): Socket {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
export interface ClientTCP_transport {
|
|
59
|
-
on(eventName: "
|
|
59
|
+
on(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
|
|
60
60
|
on(eventName: "socket_closed", eventHandler: (err: Error | null) => void): this;
|
|
61
61
|
on(eventName: "close", eventHandler: (err: Error | null) => void): this;
|
|
62
62
|
on(eventName: "connection_break", eventHandler: () => void): this;
|
|
63
63
|
|
|
64
|
-
once(eventName: "
|
|
64
|
+
once(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
|
|
65
65
|
once(eventName: "socket_closed", eventHandler: (err: Error | null) => void): this;
|
|
66
66
|
once(eventName: "close", eventHandler: (err: Error | null) => void): this;
|
|
67
67
|
once(eventName: "connection_break", eventHandler: () => void): this;
|
|
@@ -96,8 +96,8 @@ export interface ClientTCP_transport {
|
|
|
96
96
|
*
|
|
97
97
|
* ....
|
|
98
98
|
*
|
|
99
|
-
* transport.on("
|
|
100
|
-
* // do something with
|
|
99
|
+
* transport.on("chunk",function(message_chunk) {
|
|
100
|
+
* // do something with chunk from server...
|
|
101
101
|
* });
|
|
102
102
|
*
|
|
103
103
|
*
|
|
@@ -106,6 +106,11 @@ export interface ClientTCP_transport {
|
|
|
106
106
|
*
|
|
107
107
|
*/
|
|
108
108
|
export class ClientTCP_transport extends TCP_transport {
|
|
109
|
+
public static defaultMaxChunk = 0; // 0 - no limits
|
|
110
|
+
public static defaultMaxMessageSize = 0; // 0 - no limits
|
|
111
|
+
public static defaultReceiveBufferSize = 1024 * 64 * 10;
|
|
112
|
+
public static defaultSendBufferSize = 1024 * 64 * 10; // 8192 min,
|
|
113
|
+
|
|
109
114
|
public endpointUrl: string;
|
|
110
115
|
public serverUri: string;
|
|
111
116
|
public numberOfRetry: number;
|
|
@@ -121,6 +126,11 @@ export class ClientTCP_transport extends TCP_transport {
|
|
|
121
126
|
this.serverUri = "";
|
|
122
127
|
this._counter = 0;
|
|
123
128
|
this.numberOfRetry = 0;
|
|
129
|
+
|
|
130
|
+
// initially before HEL/ACK
|
|
131
|
+
this.maxChunkCount = 1;
|
|
132
|
+
this.maxMessageSize = 4 * 1024;
|
|
133
|
+
this.receiveBufferSize = 4 * 1024;
|
|
124
134
|
}
|
|
125
135
|
|
|
126
136
|
public dispose(): void {
|
|
@@ -238,6 +248,7 @@ export class ClientTCP_transport extends TCP_transport {
|
|
|
238
248
|
this._socket.once("error", _on_socket_error_for_connect);
|
|
239
249
|
this._socket.once("end", _on_socket_end_for_connect);
|
|
240
250
|
this._socket.once("connect", _on_socket_connect);
|
|
251
|
+
|
|
241
252
|
this._install_socket(this._socket);
|
|
242
253
|
}
|
|
243
254
|
|
|
@@ -280,7 +291,9 @@ export class ClientTCP_transport extends TCP_transport {
|
|
|
280
291
|
responseClass = AcknowledgeMessage;
|
|
281
292
|
_stream.rewind();
|
|
282
293
|
response = decodeMessage(_stream, responseClass);
|
|
294
|
+
|
|
283
295
|
this.parameters = response as AcknowledgeMessage;
|
|
296
|
+
this.setLimits(response as AcknowledgeMessage);
|
|
284
297
|
|
|
285
298
|
// istanbul ignore next
|
|
286
299
|
if (doTraceHelloAck) {
|
|
@@ -304,12 +317,11 @@ export class ClientTCP_transport extends TCP_transport {
|
|
|
304
317
|
// the server will receive it as message from the client
|
|
305
318
|
const helloMessage = new HelloMessage({
|
|
306
319
|
endpointUrl: this.endpointUrl,
|
|
307
|
-
maxChunkCount: 0, // 0 - no limits
|
|
308
|
-
maxMessageSize: 0, // 0 - no limits
|
|
309
|
-
|
|
310
320
|
protocolVersion: this.protocolVersion,
|
|
311
|
-
|
|
312
|
-
|
|
321
|
+
maxChunkCount: ClientTCP_transport.defaultMaxChunk,
|
|
322
|
+
maxMessageSize: ClientTCP_transport.defaultMaxMessageSize,
|
|
323
|
+
receiveBufferSize: ClientTCP_transport.defaultReceiveBufferSize,
|
|
324
|
+
sendBufferSize: ClientTCP_transport.defaultSendBufferSize
|
|
313
325
|
});
|
|
314
326
|
// istanbul ignore next
|
|
315
327
|
if (doTraceHelloAck) {
|
package/source/index.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opcua-transport
|
|
3
3
|
*/
|
|
4
|
-
export * from "./HelloMessage";
|
|
5
4
|
export * from "./AcknowledgeMessage";
|
|
6
|
-
export * from "./TCPErrorMessage";
|
|
7
5
|
export * from "./client_tcp_transport";
|
|
6
|
+
export * from "./HelloMessage";
|
|
7
|
+
export * from "./message_builder_base";
|
|
8
8
|
export * from "./server_tcp_transport";
|
|
9
|
+
export * from "./status_codes";
|
|
9
10
|
export * from "./tcp_transport";
|
|
11
|
+
export * from "./TCPErrorMessage";
|
|
10
12
|
export * from "./tools";
|
|
11
|
-
export * from "./
|
|
13
|
+
export * from "./utils";
|
|
@@ -7,14 +7,17 @@ import { assert } from "node-opcua-assert";
|
|
|
7
7
|
import { BinaryStream } from "node-opcua-binary-stream";
|
|
8
8
|
import { createFastUninitializedBuffer } from "node-opcua-buffer-utils";
|
|
9
9
|
import { readMessageHeader, SequenceHeader } from "node-opcua-chunkmanager";
|
|
10
|
-
import { make_errorLog, make_debugLog } from "node-opcua-debug";
|
|
10
|
+
import { make_errorLog, make_debugLog, make_warningLog } from "node-opcua-debug";
|
|
11
11
|
import { MessageHeader, PacketAssembler, PacketInfo } from "node-opcua-packet-assembler";
|
|
12
|
+
import { StatusCode } from "node-opcua-status-code";
|
|
12
13
|
import { get_clock_tick } from "node-opcua-utils";
|
|
14
|
+
import { StatusCodes2 } from "./status_codes";
|
|
13
15
|
|
|
14
16
|
const doPerfMonitoring = process.env.NODEOPCUADEBUG && process.env.NODEOPCUADEBUG.indexOf("PERF") >= 0;
|
|
15
17
|
|
|
16
18
|
const errorLog = make_errorLog("MessageBuilder");
|
|
17
19
|
const debugLog = make_debugLog("MessageBuilder");
|
|
20
|
+
const warningLog = make_warningLog("MessageBuilder");
|
|
18
21
|
|
|
19
22
|
export function readRawMessageHeader(data: Buffer): PacketInfo {
|
|
20
23
|
const messageHeader = readMessageHeader(new BinaryStream(data));
|
|
@@ -25,6 +28,24 @@ export function readRawMessageHeader(data: Buffer): PacketInfo {
|
|
|
25
28
|
};
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
export interface MessageBuilderBaseOptions {
|
|
32
|
+
signatureLength?: number;
|
|
33
|
+
maxMessageSize?: number;
|
|
34
|
+
maxChunkCount?: number;
|
|
35
|
+
maxChunkSize?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MessageBuilderBase {
|
|
39
|
+
on(eventName: "startChunk", eventHandler: (info: PacketInfo, data: Buffer) => void): this;
|
|
40
|
+
on(eventName: "chunk", eventHandler: (chunk: Buffer) => void): this;
|
|
41
|
+
on(eventName: "error", eventHandler: (err: Error, statusCode: StatusCode, requestId: number | null) => void): this;
|
|
42
|
+
on(eventName: "full_message_body", eventHandler: (fullMessageBody: Buffer) => void): this;
|
|
43
|
+
|
|
44
|
+
emit(eventName: "startChunk", info: PacketInfo, data: Buffer): boolean;
|
|
45
|
+
emit(eventName: "chunk", chunk: Buffer): boolean;
|
|
46
|
+
emit(eventName: "error", err: Error, statusCode: StatusCode, requestId: number | null): boolean;
|
|
47
|
+
emit(eventName: "full_message_body", fullMessageBody: Buffer): boolean;
|
|
48
|
+
}
|
|
28
49
|
/**
|
|
29
50
|
* @class MessageBuilderBase
|
|
30
51
|
* @extends EventEmitter
|
|
@@ -35,8 +56,16 @@ export function readRawMessageHeader(data: Buffer): PacketInfo {
|
|
|
35
56
|
*
|
|
36
57
|
*/
|
|
37
58
|
export class MessageBuilderBase extends EventEmitter {
|
|
38
|
-
public
|
|
39
|
-
public
|
|
59
|
+
public static defaultMaxChunkCount = 1000;
|
|
60
|
+
public static defaultMaxMessageSize = 1024 * 64;
|
|
61
|
+
public static defaultMaxChunkSize = 1024 * 8;
|
|
62
|
+
|
|
63
|
+
public readonly signatureLength: number;
|
|
64
|
+
public readonly maxMessageSize: number;
|
|
65
|
+
public readonly maxChunkCount: number;
|
|
66
|
+
public readonly maxChunkSize: number;
|
|
67
|
+
|
|
68
|
+
public readonly options: MessageBuilderBaseOptions;
|
|
40
69
|
public readonly _packetAssembler: PacketAssembler;
|
|
41
70
|
public channelId: number;
|
|
42
71
|
public totalMessageSize: number;
|
|
@@ -57,7 +86,7 @@ export class MessageBuilderBase extends EventEmitter {
|
|
|
57
86
|
private readonly _expectedChannelId: number;
|
|
58
87
|
private offsetBodyStart: number;
|
|
59
88
|
|
|
60
|
-
constructor(options?:
|
|
89
|
+
constructor(options?: MessageBuilderBaseOptions) {
|
|
61
90
|
super();
|
|
62
91
|
|
|
63
92
|
this.id = "";
|
|
@@ -69,20 +98,29 @@ export class MessageBuilderBase extends EventEmitter {
|
|
|
69
98
|
this.messageChunks = [];
|
|
70
99
|
this._expectedChannelId = 0;
|
|
71
100
|
|
|
72
|
-
options = options || {
|
|
101
|
+
options = options || {
|
|
102
|
+
maxMessageSize: 0,
|
|
103
|
+
maxChunkCount: 0,
|
|
104
|
+
maxChunkSize: 0
|
|
105
|
+
};
|
|
73
106
|
|
|
74
107
|
this.signatureLength = options.signatureLength || 0;
|
|
75
108
|
|
|
109
|
+
this.maxMessageSize = options.maxMessageSize || MessageBuilderBase.defaultMaxMessageSize;
|
|
110
|
+
this.maxChunkCount = options.maxChunkCount || MessageBuilderBase.defaultMaxChunkCount;
|
|
111
|
+
this.maxChunkSize = options.maxChunkSize || MessageBuilderBase.defaultMaxChunkSize;
|
|
112
|
+
|
|
76
113
|
this.options = options;
|
|
77
114
|
|
|
78
115
|
this._packetAssembler = new PacketAssembler({
|
|
79
|
-
minimumSizeInBytes:
|
|
80
|
-
|
|
116
|
+
minimumSizeInBytes: 8,
|
|
117
|
+
maxChunkSize: this.maxChunkSize,
|
|
118
|
+
readChunkFunc: readRawMessageHeader
|
|
81
119
|
});
|
|
82
120
|
|
|
83
|
-
this._packetAssembler.on("
|
|
121
|
+
this._packetAssembler.on("chunk", (messageChunk) => this._feed_messageChunk(messageChunk));
|
|
84
122
|
|
|
85
|
-
this._packetAssembler.on("
|
|
123
|
+
this._packetAssembler.on("startChunk", (info, data) => {
|
|
86
124
|
if (doPerfMonitoring) {
|
|
87
125
|
// record tick 0: when the first data is received
|
|
88
126
|
this._tick0 = get_clock_tick();
|
|
@@ -94,7 +132,12 @@ export class MessageBuilderBase extends EventEmitter {
|
|
|
94
132
|
* @param info
|
|
95
133
|
* @param data
|
|
96
134
|
*/
|
|
97
|
-
this.emit("
|
|
135
|
+
this.emit("startChunk", info, data);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
this._packetAssembler.on("error", (err) => {
|
|
139
|
+
warningLog("packet assembler ", err.message);
|
|
140
|
+
return this._report_error(StatusCodes2.BadTcpMessageTooLarge, "packet assembler: " + err.message);
|
|
98
141
|
});
|
|
99
142
|
|
|
100
143
|
this._securityDefeated = false;
|
|
@@ -135,15 +178,15 @@ export class MessageBuilderBase extends EventEmitter {
|
|
|
135
178
|
|
|
136
179
|
// verifying secure ChannelId
|
|
137
180
|
if (this._expectedChannelId && this.channelId !== this._expectedChannelId) {
|
|
138
|
-
return this._report_error("Invalid secure channel Id");
|
|
181
|
+
return this._report_error(StatusCodes2.BadTcpSecureChannelUnknown, "Invalid secure channel Id");
|
|
139
182
|
}
|
|
140
183
|
return true;
|
|
141
184
|
} catch (err) {
|
|
142
|
-
return
|
|
185
|
+
return this._report_error(StatusCodes2.BadTcpInternalError, "_read_headers error " + (err as Error).message);
|
|
143
186
|
}
|
|
144
187
|
}
|
|
145
188
|
|
|
146
|
-
protected _report_error(errorMessage: string): false {
|
|
189
|
+
protected _report_error(statusCode: StatusCode, errorMessage: string): false {
|
|
147
190
|
this._hasReceivedError = true;
|
|
148
191
|
/**
|
|
149
192
|
* notify the observers that an error has occurred
|
|
@@ -152,7 +195,7 @@ export class MessageBuilderBase extends EventEmitter {
|
|
|
152
195
|
*/
|
|
153
196
|
debugLog("Error ", this.id, errorMessage);
|
|
154
197
|
// xx errorLog(new Error());
|
|
155
|
-
this.emit("error", new Error(errorMessage), this.sequenceHeader ? this.sequenceHeader.requestId : null);
|
|
198
|
+
this.emit("error", new Error(errorMessage), statusCode, this.sequenceHeader ? this.sequenceHeader.requestId : null);
|
|
156
199
|
return false;
|
|
157
200
|
}
|
|
158
201
|
|
|
@@ -177,13 +220,21 @@ export class MessageBuilderBase extends EventEmitter {
|
|
|
177
220
|
return false;
|
|
178
221
|
}
|
|
179
222
|
|
|
223
|
+
if (this.messageChunks.length + 1 > this.maxChunkCount) {
|
|
224
|
+
return this._report_error(StatusCodes2.BadTcpMessageTooLarge, `max chunk count exceeded: ${this.maxChunkCount}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
180
227
|
this.messageChunks.push(chunk);
|
|
181
228
|
this.totalMessageSize += chunk.length;
|
|
182
229
|
|
|
230
|
+
if (this.totalMessageSize > this.maxMessageSize) {
|
|
231
|
+
return this._report_error(StatusCodes2.BadTcpMessageTooLarge, `max message size exceeded: ${this.maxMessageSize}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
183
234
|
const binaryStream = new BinaryStream(chunk);
|
|
184
235
|
|
|
185
236
|
if (!this._read_headers(binaryStream)) {
|
|
186
|
-
return this._report_error(`Invalid message header detected`);
|
|
237
|
+
return this._report_error(StatusCodes2.BadTcpInternalError, `Invalid message header detected`);
|
|
187
238
|
}
|
|
188
239
|
|
|
189
240
|
assert(binaryStream.length >= 12);
|
|
@@ -192,6 +243,7 @@ export class MessageBuilderBase extends EventEmitter {
|
|
|
192
243
|
if (this.messageHeader!.length !== chunk.length) {
|
|
193
244
|
// tslint:disable:max-line-length
|
|
194
245
|
return this._report_error(
|
|
246
|
+
StatusCodes2.BadTcpInternalError,
|
|
195
247
|
`Invalid messageChunk size: the provided chunk is ${chunk.length} bytes long but header specifies ${
|
|
196
248
|
this.messageHeader!.length
|
|
197
249
|
}`
|
|
@@ -254,7 +306,7 @@ export class MessageBuilderBase extends EventEmitter {
|
|
|
254
306
|
this._init_new();
|
|
255
307
|
return true;
|
|
256
308
|
} else if (messageHeader.isFinal === "A") {
|
|
257
|
-
return this._report_error("received and Abort Message");
|
|
309
|
+
return this._report_error(StatusCodes2.BadRequestInterrupted, "received and Abort Message");
|
|
258
310
|
} else if (messageHeader.isFinal === "C") {
|
|
259
311
|
return this._append(chunk);
|
|
260
312
|
}
|
|
@@ -55,12 +55,6 @@ const minimumBufferSize = 8192;
|
|
|
55
55
|
export class ServerTCP_transport extends TCP_transport {
|
|
56
56
|
public static throttleTime = 1000;
|
|
57
57
|
|
|
58
|
-
public receiveBufferSize: number;
|
|
59
|
-
public sendBufferSize: number;
|
|
60
|
-
public maxMessageSize: number;
|
|
61
|
-
public maxChunkCount: number;
|
|
62
|
-
public protocolVersion: number;
|
|
63
|
-
|
|
64
58
|
private _aborted: number;
|
|
65
59
|
private _helloReceived: boolean;
|
|
66
60
|
|
|
@@ -68,14 +62,16 @@ export class ServerTCP_transport extends TCP_transport {
|
|
|
68
62
|
super();
|
|
69
63
|
this._aborted = 0;
|
|
70
64
|
this._helloReceived = false;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
75
|
-
this.
|
|
65
|
+
|
|
66
|
+
// before HEL/ACK
|
|
67
|
+
this.maxChunkCount = 1;
|
|
68
|
+
this.maxMessageSize = 4 * 1024;
|
|
69
|
+
this.receiveBufferSize = 4 * 1024;
|
|
76
70
|
}
|
|
77
71
|
|
|
78
72
|
protected _write_chunk(messageChunk: Buffer): void {
|
|
73
|
+
|
|
74
|
+
// istanbul ignore next
|
|
79
75
|
if (this.sendBufferSize > 0 && messageChunk.length > this.sendBufferSize) {
|
|
80
76
|
errorLog(
|
|
81
77
|
"write chunk exceed sendBufferSize messageChunk length = ",
|
|
@@ -84,6 +80,7 @@ export class ServerTCP_transport extends TCP_transport {
|
|
|
84
80
|
this.sendBufferSize
|
|
85
81
|
);
|
|
86
82
|
}
|
|
83
|
+
|
|
87
84
|
super._write_chunk(messageChunk);
|
|
88
85
|
}
|
|
89
86
|
/**
|
|
@@ -115,57 +112,63 @@ export class ServerTCP_transport extends TCP_transport {
|
|
|
115
112
|
public abortWithError(statusCode: StatusCode, extraErrorDescription: string, callback: ErrorCallback): void {
|
|
116
113
|
return this._abortWithError(statusCode, extraErrorDescription, callback);
|
|
117
114
|
}
|
|
115
|
+
|
|
118
116
|
private _abortWithError(statusCode: StatusCode, extraErrorDescription: string, callback: ErrorCallback): void {
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
// When a fatal error occurs, the Server shall send an Error Message to the Client and
|
|
118
|
+
// closes the TransportConnection gracefully.
|
|
119
|
+
doDebug && debugLog(chalk.cyan("_abortWithError"));
|
|
120
|
+
|
|
121
|
+
/* istanbul ignore next */
|
|
122
|
+
if (this._aborted) {
|
|
123
|
+
// already called
|
|
124
|
+
return callback(new Error(statusCode.name));
|
|
121
125
|
}
|
|
126
|
+
this._aborted = 1;
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
// send the error message and close the connection
|
|
130
|
+
this.sendErrorMessage(statusCode, statusCode.description);
|
|
124
131
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
132
|
+
this.disconnect(() => {
|
|
133
|
+
this._aborted = 2;
|
|
134
|
+
callback(new Error(extraErrorDescription + " StatusCode = " + statusCode.name));
|
|
135
|
+
});
|
|
136
|
+
}, ServerTCP_transport.throttleTime);
|
|
137
|
+
}
|
|
131
138
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
debugLog(chalk.red(" extraErrorDescription ") + chalk.cyan(extraErrorDescription));
|
|
136
|
-
}
|
|
139
|
+
private _send_ACK_response(helloMessage: HelloMessage): void {
|
|
140
|
+
assert(helloMessage.receiveBufferSize >= minimumBufferSize);
|
|
141
|
+
assert(helloMessage.sendBufferSize >= minimumBufferSize);
|
|
137
142
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
statusCode
|
|
141
|
-
});
|
|
143
|
+
const minBufferSize = 8192;
|
|
144
|
+
const maxBufferSize = 64 * 1024;
|
|
142
145
|
|
|
143
|
-
|
|
146
|
+
const minMaxMessageSize = 128 * 1024;
|
|
147
|
+
const defaultMaxMessageSize = 16 * 1024 * 1024;
|
|
148
|
+
const maxMaxMessageSize = 128 * 1024 * 1024;
|
|
144
149
|
|
|
145
|
-
|
|
150
|
+
const minMaxChunkCount = 1;
|
|
151
|
+
const defaultMaxChunkCount = defaultMaxMessageSize / maxBufferSize;
|
|
152
|
+
const maxMaxChunkCount = 9000;
|
|
146
153
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
callback(new Error(extraErrorDescription + " StatusCode = " + statusCode.name));
|
|
150
|
-
});
|
|
151
|
-
}, ServerTCP_transport.throttleTime);
|
|
152
|
-
} else {
|
|
153
|
-
callback(new Error(statusCode.name));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
154
|
+
const receiveBufferSize = 32 * 1024;
|
|
155
|
+
const sendBufferSize = 32 * 1024;
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
if (!helloMessage.maxChunkCount && helloMessage.sendBufferSize) {
|
|
158
|
+
helloMessage.maxChunkCount = helloMessage.maxMessageSize / helloMessage.sendBufferSize;
|
|
159
|
+
}
|
|
160
160
|
|
|
161
|
-
this.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
this.setLimits({
|
|
162
|
+
receiveBufferSize: clamp_value(helloMessage.receiveBufferSize || receiveBufferSize, minBufferSize, maxBufferSize),
|
|
163
|
+
sendBufferSize: clamp_value(helloMessage.sendBufferSize || sendBufferSize, minBufferSize, maxBufferSize),
|
|
164
|
+
maxMessageSize: clamp_value(helloMessage.maxMessageSize || defaultMaxMessageSize, minMaxMessageSize, maxMaxMessageSize),
|
|
165
|
+
maxChunkCount: clamp_value(helloMessage.maxChunkCount || defaultMaxChunkCount, minMaxChunkCount, maxMaxChunkCount)
|
|
166
|
+
});
|
|
165
167
|
|
|
166
168
|
// istanbul ignore next
|
|
167
169
|
if (doTraceHelloAck) {
|
|
168
170
|
console.log(`received Hello \n${helloMessage.toString()}`);
|
|
171
|
+
console.log("Client accepts only message of size => ", this.maxMessageSize);
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
debugLog("Client accepts only message of size => ", this.maxMessageSize);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { StatusCodes } from "node-opcua-status-code";
|
|
2
|
+
|
|
3
|
+
export const StatusCodes2 = {
|
|
4
|
+
/**
|
|
5
|
+
* The Server cannot process the request because it is too busy.
|
|
6
|
+
*
|
|
7
|
+
* It is up to the Server to determine when it needs to return this Message.
|
|
8
|
+
*
|
|
9
|
+
* A Server can control the how frequently a Client reconnects by waiting to return this error.
|
|
10
|
+
*/
|
|
11
|
+
BadTcpServerTooBusy: StatusCodes.BadTcpServerTooBusy,
|
|
12
|
+
/**
|
|
13
|
+
* The type of the Message specified in the header invalid.
|
|
14
|
+
* Each Message starts with a 4-byte sequence of ASCII values that identifies the Message type.
|
|
15
|
+
* The Server returns this error if the Message type is not accepted.
|
|
16
|
+
* Some of the Message types are defined by the SecureChannel layer.
|
|
17
|
+
*/
|
|
18
|
+
BadTcpMessageTypeInvalid: StatusCodes.BadTcpMessageTypeInvalid,
|
|
19
|
+
/**
|
|
20
|
+
* The SecureChannelId and/or TokenId are not currently in use.
|
|
21
|
+
* This error is reported by the SecureChannel layer.
|
|
22
|
+
*/
|
|
23
|
+
BadTcpSecureChannelUnknown: StatusCodes.BadTcpSecureChannelUnknown,
|
|
24
|
+
/**
|
|
25
|
+
* The size of the Message specified in the header is too large.
|
|
26
|
+
* The Server returns this error if the Message size exceeds its maximum buffer size
|
|
27
|
+
* or the receive buffer size negotiated during the Hello/Acknowledge exchange.
|
|
28
|
+
*/
|
|
29
|
+
BadTcpMessageTooLarge: StatusCodes.BadTcpMessageTooLarge,
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A timeout occurred while accessing a resource.
|
|
33
|
+
* It is up to the Server to determine when a timeout occurs.
|
|
34
|
+
*/
|
|
35
|
+
BadTimeout: StatusCodes.BadTimeout,
|
|
36
|
+
/**
|
|
37
|
+
* There are not enough resources to process the request.
|
|
38
|
+
* The Server returns this error when it runs out of memory or encounters similar resource problems.
|
|
39
|
+
* A Server can control the how frequently a Client reconnects by waiting to return this error.
|
|
40
|
+
*/
|
|
41
|
+
BadTcpNotEnoughResources: StatusCodes.BadTcpNotEnoughResources,
|
|
42
|
+
/**
|
|
43
|
+
* An internal error occurred.
|
|
44
|
+
* This should only be returned if an unexpected configuration or programming error occurs.
|
|
45
|
+
*/
|
|
46
|
+
BadTcpInternalError: StatusCodes.BadTcpInternalError,
|
|
47
|
+
/**
|
|
48
|
+
* The Server does not recognize the EndpointUrl specified.
|
|
49
|
+
*/
|
|
50
|
+
BadTcpEndpointUrlInvalid: StatusCodes.BadTcpEndpointUrlInvalid,
|
|
51
|
+
/**
|
|
52
|
+
* The Message was rejected because it could not be verified.
|
|
53
|
+
*/
|
|
54
|
+
BadSecurityChecksFailed: StatusCodes.BadSecurityChecksFailed,
|
|
55
|
+
/**
|
|
56
|
+
* The request could not be sent because of a network interruption.
|
|
57
|
+
*/
|
|
58
|
+
BadRequestInterrupted: StatusCodes.BadRequestInterrupted,
|
|
59
|
+
/**
|
|
60
|
+
* Timeout occurred while processing the request.
|
|
61
|
+
*/
|
|
62
|
+
BadRequestTimeout: StatusCodes.BadRequestTimeout,
|
|
63
|
+
/**
|
|
64
|
+
* The secure channel has been closed.
|
|
65
|
+
*/
|
|
66
|
+
BadSecureChannelClosed: StatusCodes.BadSecureChannelClosed,
|
|
67
|
+
/**
|
|
68
|
+
* The SecurityToken has expired or is not recognized. BadSecureChannelTokenUnknown
|
|
69
|
+
*/
|
|
70
|
+
BadSecureChannelTokenUnknown: StatusCodes.BadSecureChannelTokenUnknown,
|
|
71
|
+
/**
|
|
72
|
+
* The sender Certificate is not trusted by the receiver.
|
|
73
|
+
*/
|
|
74
|
+
BadCertificateUntrusted: StatusCodes.BadCertificateUntrusted,
|
|
75
|
+
/**
|
|
76
|
+
* The sender Certificate has expired or is not yet valid.
|
|
77
|
+
*/
|
|
78
|
+
BadCertificateTimeInvalid: StatusCodes.BadCertificateTimeInvalid,
|
|
79
|
+
/**
|
|
80
|
+
* The issuer for the sender Certificate has expired or is not yet valid.
|
|
81
|
+
*/
|
|
82
|
+
BadCertificateIssuerTimeInvalid: StatusCodes.BadCertificateIssuerTimeInvalid,
|
|
83
|
+
/**
|
|
84
|
+
* The sender’s Certificate may not be used for establishing a secure channel.
|
|
85
|
+
*/
|
|
86
|
+
BadCertificateUseNotAllowed: StatusCodes.BadCertificateUseNotAllowed,
|
|
87
|
+
/**
|
|
88
|
+
* The issuer Certificate may not be used as a Certificate Authority.
|
|
89
|
+
*/
|
|
90
|
+
BadCertificateIssuerUseNotAllowed: StatusCodes.BadCertificateIssuerUseNotAllowed,
|
|
91
|
+
/**
|
|
92
|
+
* Could not verify the revocation status of the sender’s Certificate.
|
|
93
|
+
*/
|
|
94
|
+
BadCertificateRevocationUnknown: StatusCodes.BadCertificateRevocationUnknown,
|
|
95
|
+
/**
|
|
96
|
+
* Could not verify the revocation status of the issuer Certificate.
|
|
97
|
+
*/
|
|
98
|
+
BadCertificateIssuerRevocationUnknown: StatusCodes.BadCertificateIssuerRevocationUnknown,
|
|
99
|
+
/**
|
|
100
|
+
* The sender Certificate has been revoked by the issuer.
|
|
101
|
+
*/
|
|
102
|
+
BadCertificateRevoked: StatusCodes.BadCertificateRevoked
|
|
103
|
+
/**The issuer Certificate has been revoked by its issuer.
|
|
104
|
+
*/
|
|
105
|
+
// todo BadIssuerCertificateRevoked: StatusCodes.BadIssuerCertificateRevoked,
|
|
106
|
+
/** The receiver Certificate thumbprint is not recognized by the receiver.
|
|
107
|
+
*/
|
|
108
|
+
// todo .. BadCertificateUnknown: StatusCodes.BadCertificateUnknown
|
|
109
|
+
};
|