@interopio/bridge 0.0.1-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/bin/bridge.js +9 -0
  2. package/gen/instance/GeneratedBuildInfo.ts +4 -0
  3. package/license.md +5 -0
  4. package/package.json +40 -0
  5. package/readme.md +10 -0
  6. package/src/cluster/Address.ts +57 -0
  7. package/src/cluster/Cluster.ts +13 -0
  8. package/src/cluster/Endpoint.ts +5 -0
  9. package/src/cluster/Member.ts +9 -0
  10. package/src/cluster/MembershipListener.ts +6 -0
  11. package/src/config/Config.ts +100 -0
  12. package/src/config/DiscoveryConfig.ts +21 -0
  13. package/src/config/Duration.ts +168 -0
  14. package/src/config/KubernetesConfig.ts +7 -0
  15. package/src/config/NamedDiscoveryConfig.ts +17 -0
  16. package/src/config/Properties.ts +49 -0
  17. package/src/config/index.ts +1 -0
  18. package/src/discovery/SimpleDiscoveryNode.ts +14 -0
  19. package/src/discovery/index.ts +207 -0
  20. package/src/discovery/multicast/MulticastDiscoveryStrategy.ts +141 -0
  21. package/src/discovery/multicast/MulticastDiscoveryStrategyFactory.ts +30 -0
  22. package/src/discovery/multicast/MulticastProperties.ts +4 -0
  23. package/src/discovery/settings.ts +37 -0
  24. package/src/error/RequestFailure.ts +48 -0
  25. package/src/gossip/ApplicationState.ts +48 -0
  26. package/src/gossip/EndpointState.ts +141 -0
  27. package/src/gossip/FailureDetector.ts +235 -0
  28. package/src/gossip/Gossiper.ts +1133 -0
  29. package/src/gossip/HeartbeatState.ts +66 -0
  30. package/src/gossip/Messenger.ts +130 -0
  31. package/src/gossip/VersionedValue.ts +59 -0
  32. package/src/index.ts +3 -0
  33. package/src/instance/AddressPicker.ts +245 -0
  34. package/src/instance/BridgeNode.ts +141 -0
  35. package/src/instance/ClusterTopologyIntentTracker.ts +4 -0
  36. package/src/io/VersionedSerializer.ts +230 -0
  37. package/src/io/util.ts +117 -0
  38. package/src/kubernetes/DnsEndpointResolver.ts +70 -0
  39. package/src/kubernetes/KubernetesApiEndpointResolver.ts +111 -0
  40. package/src/kubernetes/KubernetesApiProvider.ts +75 -0
  41. package/src/kubernetes/KubernetesClient.ts +264 -0
  42. package/src/kubernetes/KubernetesConfig.ts +130 -0
  43. package/src/kubernetes/KubernetesDiscoveryStrategy.ts +30 -0
  44. package/src/kubernetes/KubernetesDiscoveryStrategyFactory.ts +71 -0
  45. package/src/kubernetes/KubernetesEndpointResolver.ts +43 -0
  46. package/src/kubernetes/KubernetesProperties.ts +22 -0
  47. package/src/license/BridgeLicenseValidator.ts +19 -0
  48. package/src/license/LicenseValidator.ts +114 -0
  49. package/src/license/types.ts +40 -0
  50. package/src/logging.ts +22 -0
  51. package/src/main.mts +53 -0
  52. package/src/net/Action.ts +143 -0
  53. package/src/net/AddressSerializer.ts +44 -0
  54. package/src/net/ByteBufferAllocator.ts +27 -0
  55. package/src/net/FrameDecoder.ts +314 -0
  56. package/src/net/FrameEncoder.ts +138 -0
  57. package/src/net/HandshakeProtocol.ts +143 -0
  58. package/src/net/InboundConnection.ts +108 -0
  59. package/src/net/InboundConnectionInitiator.ts +150 -0
  60. package/src/net/InboundMessageHandler.ts +377 -0
  61. package/src/net/InboundSink.ts +38 -0
  62. package/src/net/Message.ts +428 -0
  63. package/src/net/OutboundConnection.ts +1141 -0
  64. package/src/net/OutboundConnectionInitiator.ts +76 -0
  65. package/src/net/RequestCallbacks.ts +148 -0
  66. package/src/net/ResponseHandler.ts +30 -0
  67. package/src/net/ShareableBytes.ts +125 -0
  68. package/src/net/internal/AsyncResourceExecutor.ts +464 -0
  69. package/src/net/internal/AsyncSocketPromise.ts +37 -0
  70. package/src/net/internal/channel/ChannelHandlerAdapter.ts +99 -0
  71. package/src/net/internal/channel/types.ts +188 -0
  72. package/src/utils/bigint.ts +23 -0
  73. package/src/utils/buffer.ts +434 -0
  74. package/src/utils/clock.ts +148 -0
  75. package/src/utils/collections.ts +283 -0
  76. package/src/utils/crc.ts +39 -0
  77. package/src/utils/internal/IpAddressUtil.ts +161 -0
  78. package/src/utils/memory/BufferPools.ts +40 -0
  79. package/src/utils/network.ts +130 -0
  80. package/src/utils/promise.ts +38 -0
  81. package/src/utils/uuid.ts +5 -0
  82. package/src/utils/vint.ts +238 -0
  83. package/src/version/MemberVersion.ts +42 -0
  84. package/src/version/Version.ts +12 -0
@@ -0,0 +1,314 @@
1
+ import {shareableBytes, type ShareableBytes} from './ShareableBytes.ts';
2
+ import type {ByteBufferAllocator} from './ByteBufferAllocator.ts';
3
+ import {type ByteBuffer} from '../utils/buffer.ts';
4
+ import {computeCrc32, crc24} from '../utils/crc.ts';
5
+
6
+ export type ValidFrame = { corrupted: false, size: number, selfContained: boolean, contents: ShareableBytes };
7
+ export type CorruptedFrame
8
+ = { corrupted: true, recoverable: true, size: number, selfContained: boolean, readCrc: number, computedCrc: number }
9
+ | { corrupted: true, recoverable: false, readCrc: number, computedCrc: number };
10
+ export type Frame = ValidFrame | CorruptedFrame;
11
+ export type FrameProcessor = (frame: Frame) => boolean;
12
+ const HEADER_LENGTH = 6;
13
+ const TRAILER_LENGTH = 4;
14
+ const HEADER_AND_TRAILER_LENGTH = HEADER_LENGTH + TRAILER_LENGTH;
15
+
16
+ const TMP = new DataView(new ArrayBuffer(8));
17
+ export function reverseBytes32(i: number): number {
18
+ TMP.setInt32(0, i);
19
+ return TMP.getInt32(0, true);
20
+ }
21
+ function reverseBytes64(i: bigint): bigint {
22
+ TMP.setBigInt64(0, i);
23
+ return TMP.getBigInt64(0, true);
24
+ }
25
+
26
+ // fill target from source up to targetPosition
27
+ function copyToSize(source: ByteBuffer, target: ByteBuffer, targetPosition: number): boolean {
28
+ const length = targetPosition - target.position;
29
+ if (length < 0) {
30
+ return true;
31
+ }
32
+ const extra = source.remaining - length;
33
+ if (extra <= 0) {
34
+ target.setByteBuffer(source);
35
+ return false;
36
+ }
37
+ source.limit = source.position + length;
38
+ target.setByteBuffer(source);
39
+ source.limit = source.position + extra;
40
+ return true;
41
+ }
42
+
43
+ function readHeader6b(frame: ByteBuffer, begin: number): bigint {
44
+ let header6b: bigint;
45
+ if (frame.limit - begin >= 8) {
46
+ header6b = frame.getBigInt64();
47
+ if (frame.order === 'big-endian') {
48
+ header6b = reverseBytes64(header6b);
49
+ }
50
+ header6b &= 0xffffffffffffn;
51
+ } else {
52
+ header6b = 0n;
53
+ for (let i = 0; i < HEADER_LENGTH; i++) {
54
+ header6b |= (BigInt(frame.getInt8(begin + i))) << BigInt(i * 8);
55
+ }
56
+ }
57
+ return header6b;
58
+ }
59
+
60
+ function isSelfContained(header6b: bigint): boolean {
61
+ return (header6b & BigInt(1 << 17)) !== 0n;
62
+ }
63
+
64
+ function payloadLength(header6b: bigint): number {
65
+ return Number(header6b & 0x0ffffffffn) & 0x1FFFF;
66
+ }
67
+
68
+ function headerCrc(header6b: bigint): number {
69
+ return Number(((header6b >> 24n) & 0x0ffffffffffn)) & 0xFFFFFF;
70
+ }
71
+
72
+ function verifyHeader6b(header6b: bigint): CorruptedFrame | undefined {
73
+ const computeLengthCrc = crc24(header6b, 3);
74
+ const readLengthCrc = headerCrc(header6b);
75
+ return readLengthCrc === computeLengthCrc ? undefined : {corrupted: true, recoverable: false, readCrc: readLengthCrc, computedCrc: computeLengthCrc};
76
+ }
77
+
78
+
79
+ abstract class FrameDecoder {
80
+ protected allocator: ByteBufferAllocator;
81
+
82
+ private readonly frames: Frame[] = [];
83
+ private active: boolean = false;
84
+
85
+ constructor(allocator: ByteBufferAllocator) {
86
+ this.allocator = allocator;
87
+ }
88
+ private processor: FrameProcessor = (frame) => {
89
+ throw new Error('processor not set')
90
+ };
91
+
92
+ activate(processor: FrameProcessor): void {
93
+ this.processor = processor;
94
+ this.active = true;
95
+ }
96
+
97
+ reactivate(): void {
98
+ if (this.active) {
99
+ throw new Error('already active');
100
+ }
101
+
102
+ if (this.deliver(this.processor)) {
103
+ this.active = true;
104
+ }
105
+ }
106
+
107
+ discard(): void {
108
+ this.active = false;
109
+ this.processor = (frame) => {throw new Error('frame processor closed')};
110
+ if (this._stash) {
111
+ const bytes = this._stash;
112
+ delete this._stash;
113
+ this.allocator.put(bytes);
114
+ }
115
+ while (this.frames.length > 0) {
116
+ const frame = this.frames.shift();
117
+ FrameDecoder.releaseFrame(frame);
118
+ }
119
+ }
120
+
121
+ private static isFrameConsumed(frame: Frame): boolean {
122
+ if (frame.corrupted === false) {
123
+ return !frame.contents.hasRemaining;
124
+ }
125
+ return true;
126
+ }
127
+
128
+ private static releaseFrame(frame: Frame): void {
129
+ if (frame.corrupted === false) {
130
+ frame.contents.release();
131
+ }
132
+ // else, corrupted frames are not released
133
+ }
134
+
135
+ private deliver(process: FrameProcessor): boolean {
136
+
137
+ let deliver = true;
138
+ while (deliver && this.frames.length > 0) {
139
+ const frame = this.frames[0];
140
+ deliver = process(frame);
141
+ if (deliver && !FrameDecoder.isFrameConsumed(frame)) {
142
+ throw new Error('frame not fully consumed');
143
+ }
144
+ if (deliver || FrameDecoder.isFrameConsumed(frame)) {
145
+ this.frames.shift();
146
+ FrameDecoder.releaseFrame(frame);
147
+ }
148
+ }
149
+ return deliver;
150
+ }
151
+
152
+ private decode(frames: Frame[], bytes: ShareableBytes): void {
153
+ this.doDecode(frames, bytes, HEADER_LENGTH);
154
+ }
155
+
156
+ private _stash: ByteBuffer;
157
+
158
+ private stash(input: ShareableBytes, stashLength: number, begin: number, length: number): void {
159
+ const out = this.allocator.getAtLeast(stashLength);
160
+ const array = new Uint8Array(length);
161
+ input.get().getUint8Array(array, begin, length);
162
+ out.setUint8Array(array);
163
+ out.position = length;
164
+ this._stash = out;
165
+ }
166
+
167
+ protected abstract readHeader(frame: ByteBuffer, begin: number): bigint
168
+
169
+ protected abstract verifyHeader(header: bigint): CorruptedFrame | undefined;
170
+
171
+ protected abstract frameLength(header: bigint): number;
172
+
173
+ protected abstract unpackFrame(frame: ShareableBytes, begin: number, end: number, header: bigint): Frame;
174
+
175
+ protected doDecode(frames: Frame[], newBytes: ShareableBytes, headerLength: number): void {
176
+ const input = newBytes.get();
177
+ try {
178
+ if (this._stash !== undefined) {
179
+ if (!copyToSize(input, this._stash, headerLength)) {
180
+ return;
181
+ }
182
+ const header = this.readHeader(this._stash, 0);
183
+ const c = this.verifyHeader(header);
184
+ if (c !== undefined) {
185
+ this.discard();
186
+ frames.push(c);
187
+ return;
188
+ }
189
+ const frameLength = this.frameLength(header);
190
+ this._stash = this.ensureCapacity(this._stash, frameLength);
191
+
192
+ if (!copyToSize(input, this._stash, frameLength)) {
193
+ return;
194
+ }
195
+
196
+ const stashed = shareableBytes(this._stash);
197
+
198
+ this._stash.flip();
199
+ delete this._stash;
200
+
201
+ try {
202
+ frames.push(this.unpackFrame(stashed, 0, frameLength, header));
203
+ } finally {
204
+ stashed.release();
205
+ }
206
+ }
207
+
208
+ let begin = input.position;
209
+ const limit = input.limit;
210
+ while (begin < limit) {
211
+ const remaining = limit - begin;
212
+ if (remaining < headerLength) {
213
+ this.stash(newBytes, headerLength, begin, remaining);
214
+ return;
215
+ }
216
+
217
+ const header = this.readHeader(input, begin);
218
+ const c = this.verifyHeader(header);
219
+ if (c !== undefined) {
220
+ frames.push(c);
221
+ return;
222
+ }
223
+
224
+ const frameLength = this.frameLength(header);
225
+ if (remaining < frameLength) {
226
+ this.stash(newBytes, frameLength, begin, remaining);
227
+ return;
228
+ }
229
+
230
+ frames.push(this.unpackFrame(newBytes, begin, begin + frameLength, header));
231
+ begin += frameLength;
232
+ }
233
+ } finally {
234
+ newBytes.release();
235
+ }
236
+ }
237
+
238
+
239
+ receive(buffer: ByteBuffer) {
240
+ this.decode(this.frames, shareableBytes(buffer));
241
+ if (this.active) {
242
+ this.active = this.deliver(this.processor);
243
+ }
244
+ }
245
+
246
+ private ensureCapacity(buffer: ByteBuffer, capacity: number) {
247
+ if (buffer.capacity >= capacity) {
248
+ return buffer;
249
+ }
250
+ const newBuffer = this.allocator.getAtLeast(capacity);
251
+ buffer.flip();
252
+ newBuffer.setByteBuffer(buffer);
253
+ this.allocator.put(buffer);
254
+ return newBuffer;
255
+ }
256
+ }
257
+
258
+ class FrameDecoderCrc extends FrameDecoder {
259
+
260
+ protected readHeader(frame: ByteBuffer, begin: number): bigint {
261
+ return readHeader6b(frame, begin);
262
+ }
263
+
264
+ protected verifyHeader(header: bigint): CorruptedFrame | undefined {
265
+ return verifyHeader6b(header);
266
+ }
267
+
268
+ protected frameLength(header: bigint): number {
269
+ return payloadLength(header) + HEADER_AND_TRAILER_LENGTH;
270
+ }
271
+
272
+ protected unpackFrame(bytes: ShareableBytes, begin: number, end: number, header6b: bigint): Frame {
273
+ const input = bytes.get();
274
+ const selfContained = isSelfContained(header6b);
275
+ let readFullCrc = input.getInt32(end - TRAILER_LENGTH);
276
+ if (input.order === 'big-endian') {
277
+ readFullCrc = reverseBytes32(readFullCrc);
278
+ }
279
+ const computedCrc = computeCrc32(input, begin + HEADER_LENGTH, end - TRAILER_LENGTH);
280
+
281
+ if (computedCrc !== readFullCrc) {
282
+ return {corrupted: true, recoverable: true, size: end - begin, readCrc: readFullCrc, computedCrc, selfContained};
283
+ }
284
+
285
+ const contents = bytes.slice(begin + HEADER_LENGTH, end - TRAILER_LENGTH);
286
+ return {corrupted: false, contents, size: contents.remaining, selfContained};
287
+ }
288
+ }
289
+
290
+ class FrameDecoderUnprotected extends FrameDecoder {
291
+ protected readHeader(frame: ByteBuffer, begin: number): bigint {
292
+ return readHeader6b(frame, begin);
293
+ }
294
+ protected verifyHeader(header: bigint): CorruptedFrame | undefined {
295
+ return verifyHeader6b(header);
296
+ }
297
+ protected frameLength(header: bigint): number {
298
+ return payloadLength(header) + HEADER_LENGTH;
299
+ }
300
+
301
+ protected unpackFrame(bytes: ShareableBytes, begin: number, end: number, header6b: bigint): Frame {
302
+ const selfContained = isSelfContained(header6b);
303
+ const contents = bytes.slice(begin + HEADER_LENGTH, end);
304
+ return {corrupted: false, contents, size: end - begin, selfContained};
305
+ }
306
+ }
307
+
308
+ export type {FrameDecoder};
309
+ export function createUnprotected(allocator: ByteBufferAllocator): FrameDecoder {
310
+ return new FrameDecoderUnprotected(allocator);
311
+ }
312
+ export function createCrc(allocator: ByteBufferAllocator): FrameDecoder {
313
+ return new FrameDecoderCrc(allocator);
314
+ }
@@ -0,0 +1,138 @@
1
+ import {allocate, type ByteBuffer} from '../utils/buffer.ts';
2
+ import {computeCrc32, crc24} from '../utils/crc.ts';
3
+ import {reverseBytes32} from './FrameDecoder.ts';
4
+
5
+ import {ChannelHandlerAdapter} from './internal/channel/ChannelHandlerAdapter.ts';
6
+
7
+ const HEADER_LENGTH = 6;
8
+ const TRAILER_LENGTH = 4;
9
+ const HEADER_AND_TRAILER_LENGTH = HEADER_LENGTH + TRAILER_LENGTH;
10
+
11
+ function put3b(frame: ByteBuffer, index: number, value: number) {
12
+ frame.setInt8(index, value);
13
+ frame.setInt8(index + 1, value >>> 8);
14
+ frame.setInt8(index + 2, value >>> 16);
15
+ }
16
+
17
+ function writeHeader(frame: ByteBuffer, selfContained: boolean, payloadLength: number) {
18
+ let header3b: bigint = BigInt(payloadLength);
19
+ if (selfContained) {
20
+ header3b |= BigInt(1 << 17);
21
+ }
22
+ const crc = crc24(header3b, 3);
23
+ put3b(frame, 0, Number(header3b));
24
+ put3b(frame, 3, crc);
25
+ }
26
+
27
+ abstract class FrameEncoder extends ChannelHandlerAdapter {
28
+ get allocator(): PayloadAllocator {
29
+ return (capacity: number, selfContained = true) => new Payload(selfContained, capacity);
30
+ }
31
+
32
+ write(payload: Payload) {
33
+ this.encode(payload.selfContained, payload.buffer);
34
+ }
35
+
36
+ abstract encode(selfContained:boolean, buffer: ByteBuffer): void;
37
+ }
38
+
39
+ class FrameEncoderUnprotected extends FrameEncoder {
40
+
41
+ get allocator(): PayloadAllocator {
42
+ return (capacity: number, selfContained = true) => new Payload(selfContained, capacity, HEADER_LENGTH);
43
+ }
44
+
45
+ encode(selfContained: boolean, frame: ByteBuffer) {
46
+ const frameLength = frame.remaining;
47
+ const dataLength = frameLength - HEADER_LENGTH;
48
+ if (dataLength >= 1 << 17) {
49
+ throw new Error(`maximum frame size 128KiB exceeded: ${dataLength}`);
50
+ }
51
+ writeHeader(frame, selfContained, dataLength);
52
+ }
53
+ }
54
+
55
+ class FrameEncoderCrc extends FrameEncoder {
56
+ get allocator(): PayloadAllocator {
57
+ return (capacity: number, selfContained = true) => new Payload(selfContained, capacity, HEADER_LENGTH, TRAILER_LENGTH);
58
+ }
59
+
60
+ encode(selfContained: boolean, frame: ByteBuffer) {
61
+ try {
62
+ const frameLength = frame.remaining;
63
+ const dataLength = frameLength - HEADER_AND_TRAILER_LENGTH;
64
+ if (dataLength >= 1 << 17) {
65
+ throw new Error(`maximum frame size 128KiB exceeded: ${dataLength}`);
66
+ }
67
+ writeHeader(frame, selfContained, dataLength);
68
+ let frameCrc = computeCrc32(frame, HEADER_LENGTH, HEADER_LENGTH + dataLength);
69
+ if (frame.order === 'big-endian') {
70
+ frameCrc = reverseBytes32(frameCrc)
71
+ }
72
+ frame.limit = frameLength;
73
+ frame.setInt32(frameLength - TRAILER_LENGTH, frameCrc);
74
+ frame.position = 0;
75
+
76
+ }
77
+ catch (e) {
78
+ // frame.release();
79
+ throw e;
80
+ }
81
+ }
82
+ }
83
+
84
+ class Payload {
85
+ readonly selfContained: boolean;
86
+ readonly buffer: ByteBuffer;
87
+ readonly headerLength: number;
88
+ readonly trailerLength: number;
89
+ private _finished = false;
90
+
91
+ constructor(selfContained: boolean, payloadLength: number, headerLength: number = 0, trailerLength = 0) {
92
+ this.selfContained = selfContained;
93
+ this.headerLength = headerLength;
94
+ this.trailerLength = trailerLength;
95
+ this.buffer = allocate(payloadLength + headerLength + trailerLength);
96
+ this.buffer.position = headerLength;
97
+ this.buffer.limit = this.buffer.capacity - trailerLength;
98
+ }
99
+
100
+ get length(): number {
101
+ if (this._finished) {
102
+ throw new Error('already finished');
103
+ }
104
+ return this.buffer.position - this.headerLength;
105
+ }
106
+
107
+ get remaining(): number {
108
+ if (this._finished) {
109
+ throw new Error('already finished');
110
+ }
111
+ return this.buffer.remaining;
112
+ }
113
+
114
+ trim(length: number): void {
115
+ if (this._finished) {
116
+ throw new Error('already finished');
117
+ }
118
+ this.buffer.position = length + this.headerLength;
119
+ }
120
+
121
+ finish() {
122
+ if (this._finished) {
123
+ throw new Error('already finished');
124
+ }
125
+ this._finished = true;
126
+ this.buffer.limit = this.buffer.position + this.trailerLength;
127
+ this.buffer.position = 0;
128
+ }
129
+
130
+ release() {
131
+
132
+ }
133
+ }
134
+
135
+ export type {Payload};
136
+ export type PayloadAllocator = (capacity: number, selfContained?: boolean) => Payload;
137
+ export type {FrameEncoder};
138
+ export const encoder: FrameEncoder = new FrameEncoderCrc();
@@ -0,0 +1,143 @@
1
+ import {type Address} from '../cluster/Address.ts';
2
+ import {ByteBufferInput, ByteBufferOutput} from '../io/VersionedSerializer.ts';
3
+ import {addressSerializer} from './AddressSerializer.ts'
4
+ import {allocate, type ByteBuffer} from '../utils/buffer.ts';
5
+ import {computeCrc32} from '../utils/crc.ts';
6
+
7
+
8
+ /**
9
+ * The initial message sent when a node creates a connection to a remote node.
10
+ * - Magic
11
+ * - Flags
12
+ * - Version
13
+ * - Broadcast Address
14
+ *
15
+ */
16
+
17
+
18
+ export type AcceptVersions = Readonly<{min: number, max: number}>
19
+
20
+ const FramingTypes = {
21
+ UNPROTECTED: 0,
22
+ // LZ4: 1
23
+ CRC: 2
24
+ }
25
+ export type Framing = keyof typeof FramingTypes;
26
+
27
+ const MIN_LENGTH = 8;
28
+ const ACCEPT_MAX_LENGTH = 12;
29
+ const INITIATE_MAX_LENGTH = 12 + addressSerializer.MAX_SIZE;
30
+
31
+ function getBits(packed: number, start: number, count: number): number {
32
+ return (packed >>> start) & ~(-1 << count);
33
+ }
34
+
35
+ export class Initiate {
36
+ readonly from: Address;
37
+ readonly framing: Framing;
38
+ readonly acceptVersions: AcceptVersions;
39
+ constructor(from: Address, framing: Framing, acceptVersions: AcceptVersions) {
40
+ this.from = from;
41
+ this.framing = framing;
42
+ this.acceptVersions = acceptVersions;
43
+ }
44
+
45
+ private encodeFlags(): number {
46
+ let flags = 0;
47
+ flags |= 0x2; // MESSAGING SMALL
48
+ flags |= (FramingTypes[this.framing] & 0x1) << 2 | (FramingTypes[this.framing] & 0x2) << 3;
49
+ flags |= this.acceptVersions.min << 8;
50
+ flags |= this.acceptVersions.min << 16;
51
+ flags |= this.acceptVersions.max << 24;
52
+ return flags;
53
+ }
54
+
55
+ encode(): ByteBuffer {
56
+ const buffer = allocate(INITIATE_MAX_LENGTH);
57
+ const out = new ByteBufferOutput(buffer);
58
+ out.writeUint32(0xCA552DFA);
59
+ out.writeUint32(this.encodeFlags());
60
+ addressSerializer.serialize(this.from, out, this.acceptVersions.min);
61
+ out.writeUint32(computeCrc32(buffer, 0, buffer.position));
62
+ buffer.flip();
63
+ return buffer;
64
+ }
65
+
66
+ static maybeDecode(buffer: ByteBuffer): Initiate | undefined {
67
+ if (buffer.remaining < MIN_LENGTH) {
68
+ return;
69
+ }
70
+ const position = buffer.position;
71
+ const input = new ByteBufferInput(buffer, false);
72
+ const magic = input.readInt32();
73
+
74
+ const flags = input.readInt32();
75
+ if (getBits(flags, 8, 8) < 12) {
76
+ return;
77
+ }
78
+
79
+ const minVersion = getBits(flags, 16, 8);
80
+ const maxVersion = getBits(flags, 24, 8);
81
+
82
+ if (maxVersion < 12) {
83
+ return;
84
+ }
85
+
86
+ const framingBits = getBits(flags, 2, 1) | (getBits(flags, 4, 1) << 1);
87
+ const framing = framingBits === 0 ? 'UNPROTECTED' : framingBits === 2 ? 'CRC': undefined;
88
+ const stream = getBits(flags, 3, 1) === 1;
89
+
90
+ const from = addressSerializer.deserialize(input, minVersion);
91
+
92
+ const computed = computeCrc32(buffer, position, position + buffer.position);
93
+ const read = input.readInt32();
94
+ if (computed !== read) {
95
+ throw new Error('crc32 mismatch');
96
+ }
97
+
98
+ return new Initiate(from, framing, {min: minVersion, max: maxVersion});
99
+ }
100
+ }
101
+
102
+ export class Accept {
103
+ readonly useMessagingVersion: number;
104
+ readonly maxMessagingVersion: number;
105
+
106
+ constructor(useMessagingVersion: number, maxMessagingVersion: number) {
107
+ this.useMessagingVersion = useMessagingVersion;
108
+ this.maxMessagingVersion = maxMessagingVersion;
109
+ }
110
+
111
+ encode(): ByteBuffer {
112
+ const buffer = allocate(ACCEPT_MAX_LENGTH);
113
+ const out = new ByteBufferOutput(buffer);
114
+ out.writeUint32(this.maxMessagingVersion);
115
+ out.writeUint32(this.useMessagingVersion);
116
+ out.writeUint32(computeCrc32(buffer, 0, 8));
117
+ buffer.flip();
118
+ return buffer;
119
+ }
120
+ static maybeDecode(buffer: ByteBuffer): Accept | undefined {
121
+ const position = buffer.position;
122
+ if (buffer.remaining < 4) {
123
+ return;
124
+ }
125
+ const maxMessagingVersion = buffer.getUint32();
126
+ if (maxMessagingVersion < 12) {
127
+ return;
128
+ }
129
+
130
+ if (buffer.remaining < 8) {
131
+ buffer.position = position;
132
+ return;
133
+ }
134
+ const useMessagingVersion = buffer.getUint32();
135
+
136
+ const computed = computeCrc32(buffer, position, position + 8);
137
+ const read = buffer.getUint32();
138
+ if (computed !== read) {
139
+ throw new Error('crc32 mismatch');
140
+ }
141
+ return new Accept(useMessagingVersion, maxMessagingVersion);
142
+ }
143
+ }
@@ -0,0 +1,108 @@
1
+ import {type Server, type Socket} from 'node:net';
2
+ import type {Address} from '../cluster/Address.ts';
3
+ import {deferred, type DeferredPromise} from '../utils/promise.ts';
4
+ import getLogger from '../logging.ts';
5
+ import {bind} from './InboundConnectionInitiator.ts';
6
+ import type {AcceptVersions} from './HandshakeProtocol.ts';
7
+ import type {FrameDecoder} from './FrameDecoder.ts';
8
+
9
+ const logger = getLogger('net.inbound');
10
+
11
+ export type InboundConnectionSettings = {
12
+ bindAddress: Address,
13
+ handlers: (from: Address) => { handle: (decoder: FrameDecoder, socket: Socket, version: number) => void },
14
+ acceptVersions?: AcceptVersions,
15
+ handshakeTimeout?: number,
16
+ };
17
+
18
+ export class InboundSocket {
19
+ readonly settings: InboundConnectionSettings;
20
+ private listen: Server;
21
+ private binding: DeferredPromise<Server>;
22
+ private closedBeforeOpen: boolean;
23
+ private closePromise: Promise<void>;
24
+
25
+ private readonly connections: Socket[] = [];
26
+
27
+ constructor(settings: InboundConnectionSettings) {
28
+ this.settings = settings;
29
+ }
30
+
31
+ async open(): Promise<void> {
32
+ if (this.listen) {
33
+ return;
34
+ }
35
+ if (this.binding) {
36
+ await this.binding.promise;
37
+ return;
38
+ }
39
+ if (this.closedBeforeOpen) {
40
+ throw new Error("Socket closed before open");
41
+ }
42
+ this.binding = bind(this.settings, this.connections);
43
+
44
+ const d = deferred<void>();
45
+ this.binding.promise
46
+ .catch((reason) => {
47
+ delete this.binding;
48
+ d.failure(reason);
49
+ })
50
+ .then((server: Server) => {
51
+ this.listen = server;
52
+ delete this.binding;
53
+ d.success();
54
+ })
55
+ await d.promise;
56
+ }
57
+
58
+ close(shutdownListeners?: () => void): Promise<void> {
59
+
60
+ const done = deferred<void>();
61
+
62
+ const close = () => {
63
+ const closing = [];
64
+ if (this.listen !== undefined) {
65
+ closing.push(new Promise<void>((resolve, reject) => {
66
+ this.listen.close((err?) => {
67
+ if (err) {
68
+ reject(err);
69
+ } else {
70
+ resolve();
71
+ }
72
+ });
73
+ }));
74
+ }
75
+ this.connections.forEach((socket) => {
76
+ closing.push(new Promise<void>((resolve, _reject) => {
77
+ socket.end(resolve);
78
+ }));
79
+ });
80
+ Promise.all(closing)
81
+ .then(() => {
82
+ done.success();
83
+ }).catch((err) => {
84
+ done.failure(err);
85
+ });
86
+ };
87
+
88
+ if (this.listen === undefined && this.binding === undefined) {
89
+ this.closedBeforeOpen = true;
90
+ return Promise.resolve();
91
+ }
92
+ if (this.closePromise !== undefined) {
93
+ return this.closePromise;
94
+ }
95
+ this.closePromise = done.promise;
96
+
97
+ if (this.listen !== undefined) {
98
+ close();
99
+ } else {
100
+ this.binding.cancel();
101
+ this.binding.promise.finally(() => close())
102
+ }
103
+
104
+ return done.promise;
105
+
106
+ }
107
+
108
+ }