infinispan 0.14.0 → 0.16.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/.githooks/commit-msg +9 -0
- package/.githooks/configure-hooks +7 -0
- package/.githooks/configure-hooks.bat +15 -0
- package/.gitmessage +7 -0
- package/AI-CODE.md +86 -0
- package/README.md +53 -75
- package/SECURITY.md +27 -0
- package/lib/codec.js +50 -1
- package/lib/functional.js +25 -0
- package/lib/infinispan.js +299 -18
- package/lib/io.js +3 -1
- package/lib/listeners.js +116 -2
- package/lib/protocols.js +168 -20
- package/lib/transaction.js +123 -0
- package/lib/uri.js +206 -0
- package/package.json +8 -3
- package/server/.keep +0 -0
- package/types/index.d.ts +44 -0
- package/Dockerfile.server +0 -8
- package/docker-compose.yml +0 -272
- package/gen-asciidoc.sh +0 -46
- package/run-docker-testsuite.sh +0 -72
package/lib/protocols.js
CHANGED
|
@@ -23,6 +23,15 @@
|
|
|
23
23
|
var INFINITE_LIFESPAN = 0x01, INFINITE_MAXIDLE = 0x02; // Duration flag masks
|
|
24
24
|
var MAGIC = 0xA0;
|
|
25
25
|
|
|
26
|
+
var FLAGS = {
|
|
27
|
+
FORCE_RETURN_VALUE: 0x0001,
|
|
28
|
+
DEFAULT_LIFESPAN: 0x0002,
|
|
29
|
+
DEFAULT_MAXIDLE: 0x0004,
|
|
30
|
+
SKIP_CACHE_LOAD: 0x0008,
|
|
31
|
+
SKIP_INDEXING: 0x0010,
|
|
32
|
+
SKIP_LISTENER_NOTIFICATION: 0x0020
|
|
33
|
+
};
|
|
34
|
+
|
|
26
35
|
/**
|
|
27
36
|
* Creates decode actions for a key-value pair.
|
|
28
37
|
* @param {Object} decoderKey Key decoder descriptor.
|
|
@@ -129,8 +138,10 @@
|
|
|
129
138
|
};
|
|
130
139
|
|
|
131
140
|
return {
|
|
132
|
-
buildFlags: function (opts) {
|
|
133
|
-
|
|
141
|
+
buildFlags: function (opts) {
|
|
142
|
+
var result = hasOptPrev(opts) ? FLAGS.FORCE_RETURN_VALUE : 0;
|
|
143
|
+
if (hasOpt(opts, 'flags')) result |= opts.flags;
|
|
144
|
+
return result;
|
|
134
145
|
},
|
|
135
146
|
/**
|
|
136
147
|
* Returns the protocol version as a human-readable string (e.g. '3.1').
|
|
@@ -577,8 +588,8 @@
|
|
|
577
588
|
return hasPrevious(header.status);
|
|
578
589
|
},
|
|
579
590
|
isEvent: function(header) {
|
|
580
|
-
|
|
581
|
-
return
|
|
591
|
+
var op = header.opCode;
|
|
592
|
+
return op >= 0x60 && op <= 0x64;
|
|
582
593
|
},
|
|
583
594
|
isError: function(header) {
|
|
584
595
|
return header.opCode == 0x50;
|
|
@@ -699,18 +710,31 @@
|
|
|
699
710
|
|
|
700
711
|
var steps = [
|
|
701
712
|
codec.encodeString(listenerId), // listener id
|
|
702
|
-
codec.encodeUByte(includeState)
|
|
703
|
-
codec.encodeUByte(0) // TODO filter factory name
|
|
713
|
+
codec.encodeUByte(includeState) // include state
|
|
704
714
|
];
|
|
705
715
|
|
|
716
|
+
// Filter factory
|
|
717
|
+
if (_.has(opts, 'filterFactory') && _.has(opts.filterFactory, 'name')) {
|
|
718
|
+
steps.push(codec.encodeString(opts.filterFactory.name));
|
|
719
|
+
var filterParams = opts.filterFactory.params || [];
|
|
720
|
+
steps.push(codec.encodeUByte(filterParams.length));
|
|
721
|
+
filterParams.forEach(function(p) { steps.push(codec.encodeBytesWithLength(p)); });
|
|
722
|
+
} else {
|
|
723
|
+
steps.push(codec.encodeUByte(0));
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Converter factory
|
|
706
727
|
if (_.has(opts, 'converterFactory') && _.has(opts.converterFactory, 'name')) {
|
|
707
728
|
steps.push(codec.encodeString(opts.converterFactory.name));
|
|
708
|
-
|
|
729
|
+
var converterParams = opts.converterFactory.params || [];
|
|
730
|
+
steps.push(codec.encodeUByte(converterParams.length));
|
|
731
|
+
converterParams.forEach(function(p) { steps.push(codec.encodeBytesWithLength(p)); });
|
|
709
732
|
} else {
|
|
710
|
-
steps.push(codec.encodeUByte(0));
|
|
733
|
+
steps.push(codec.encodeUByte(0));
|
|
711
734
|
}
|
|
712
735
|
|
|
713
|
-
|
|
736
|
+
// Raw data
|
|
737
|
+
steps.push(codec.encodeUByte(hasOpt(opts, 'useRawData') ? 1 : 0));
|
|
714
738
|
|
|
715
739
|
return function() {
|
|
716
740
|
return steps;
|
|
@@ -1105,9 +1129,9 @@
|
|
|
1105
1129
|
var authDone = DECODE_UBYTE(bytebuf);
|
|
1106
1130
|
if(authDone == 1) {
|
|
1107
1131
|
logger.tracef('SASL authentication complete');
|
|
1108
|
-
return {result: {response: DECODE_UBYTE(bytebuf)}, continue: true};
|
|
1132
|
+
return {result: {response: DECODE_UBYTE(bytebuf), complete: true}, continue: true};
|
|
1109
1133
|
} else {
|
|
1110
|
-
return {result: {response: DECODE_STRING(bytebuf)}, continue: true};
|
|
1134
|
+
return {result: {response: DECODE_STRING(bytebuf), complete: false}, continue: true};
|
|
1111
1135
|
}
|
|
1112
1136
|
}
|
|
1113
1137
|
};
|
|
@@ -1310,6 +1334,81 @@
|
|
|
1310
1334
|
};
|
|
1311
1335
|
}());
|
|
1312
1336
|
|
|
1337
|
+
var MultimapMixin = (function() {
|
|
1338
|
+
var SUPPORTS_DUPLICATES = codec.encodeUByte(0x00);
|
|
1339
|
+
|
|
1340
|
+
return {
|
|
1341
|
+
encodeMultimapKey: function(k) {
|
|
1342
|
+
var outer = this;
|
|
1343
|
+
return function() {
|
|
1344
|
+
return [outer.encodeMediaKey(k), SUPPORTS_DUPLICATES];
|
|
1345
|
+
};
|
|
1346
|
+
},
|
|
1347
|
+
encodeMultimapPut: function(k, v) {
|
|
1348
|
+
var outer = this;
|
|
1349
|
+
return function(opts) {
|
|
1350
|
+
return f.cat(
|
|
1351
|
+
[outer.encodeMediaKey(k)],
|
|
1352
|
+
outer.encodeExpiry(opts),
|
|
1353
|
+
[outer.encodeMediaValue(v), SUPPORTS_DUPLICATES]
|
|
1354
|
+
);
|
|
1355
|
+
};
|
|
1356
|
+
},
|
|
1357
|
+
encodeMultimapKeyValue: function(k, v) {
|
|
1358
|
+
var outer = this;
|
|
1359
|
+
return function() {
|
|
1360
|
+
return f.cat(
|
|
1361
|
+
[outer.encodeMediaKey(k)],
|
|
1362
|
+
[codec.encodeUByte(0x77)],
|
|
1363
|
+
[outer.encodeMediaValue(v), SUPPORTS_DUPLICATES]
|
|
1364
|
+
);
|
|
1365
|
+
};
|
|
1366
|
+
},
|
|
1367
|
+
encodeMultimapValue: function(v) {
|
|
1368
|
+
var outer = this;
|
|
1369
|
+
return function() {
|
|
1370
|
+
return [codec.encodeUByte(0x77), outer.encodeMediaValue(v), SUPPORTS_DUPLICATES];
|
|
1371
|
+
};
|
|
1372
|
+
},
|
|
1373
|
+
encodeMultimapSupportsDuplicates: function() {
|
|
1374
|
+
return function() {
|
|
1375
|
+
return [SUPPORTS_DUPLICATES];
|
|
1376
|
+
};
|
|
1377
|
+
},
|
|
1378
|
+
decodeMultimapCollection: function() {
|
|
1379
|
+
var decoderValue = decoderMedia(this.valueMediaType);
|
|
1380
|
+
var decodeSingleValue = decodeSingle(decoderValue);
|
|
1381
|
+
return function(header, bytebuf) {
|
|
1382
|
+
if (header.status !== 0x00 && header.status !== 0x03)
|
|
1383
|
+
return {result: [], continue: true};
|
|
1384
|
+
var count = DECODE_VINT(bytebuf);
|
|
1385
|
+
if (!f.existy(count))
|
|
1386
|
+
return {continue: false};
|
|
1387
|
+
var values = [];
|
|
1388
|
+
for (var i = 0; i < count; i++) {
|
|
1389
|
+
var v = decodeSingleValue(bytebuf);
|
|
1390
|
+
if (!f.existy(v))
|
|
1391
|
+
return {continue: false};
|
|
1392
|
+
values.push(v);
|
|
1393
|
+
}
|
|
1394
|
+
return {result: values, continue: true};
|
|
1395
|
+
};
|
|
1396
|
+
},
|
|
1397
|
+
decodeMultimapBoolean: function(header, bytebuf) {
|
|
1398
|
+
var b = DECODE_UBYTE(bytebuf);
|
|
1399
|
+
if (!f.existy(b))
|
|
1400
|
+
return {continue: false};
|
|
1401
|
+
return {result: b !== 0, continue: true};
|
|
1402
|
+
},
|
|
1403
|
+
decodeMultimapSize: function(header, bytebuf) {
|
|
1404
|
+
var size = f.actions([codec.decodeVLong()], codec.lastDecoded)(bytebuf);
|
|
1405
|
+
if (!f.existy(size) && size !== 0)
|
|
1406
|
+
return {continue: false};
|
|
1407
|
+
return {result: size, continue: true};
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
}());
|
|
1411
|
+
|
|
1313
1412
|
/**
|
|
1314
1413
|
* Protocol 4.0+ requires an 'otherParams' map after media types in the header.
|
|
1315
1414
|
* This mixin overrides stepsHeader to append the count (0 = no params).
|
|
@@ -1415,6 +1514,54 @@
|
|
|
1415
1514
|
}());
|
|
1416
1515
|
|
|
1417
1516
|
|
|
1517
|
+
var DECODE_INT = f.actions([codec.decodeInt()], codec.lastDecoded);
|
|
1518
|
+
|
|
1519
|
+
var TransactionMixin = {
|
|
1520
|
+
encodeXid: function(xid) {
|
|
1521
|
+
return [
|
|
1522
|
+
codec.encodeSignedInt(xid.formatId),
|
|
1523
|
+
codec.encodeBytesWithLength(xid.globalTxId),
|
|
1524
|
+
codec.encodeBytesWithLength(xid.branchQualifier)
|
|
1525
|
+
];
|
|
1526
|
+
},
|
|
1527
|
+
encodePrepare: function(xid, modifications, onePhaseCommit, timeout) {
|
|
1528
|
+
var outer = this;
|
|
1529
|
+
return function() {
|
|
1530
|
+
var steps = outer.encodeXid(xid);
|
|
1531
|
+
steps.push(codec.encodeUByte(onePhaseCommit ? 1 : 0));
|
|
1532
|
+
steps.push(codec.encodeUByte(0)); // not recoverable
|
|
1533
|
+
steps.push(codec.encodeLong(timeout));
|
|
1534
|
+
steps.push(codec.encodeVInt(modifications.length));
|
|
1535
|
+
for (var i = 0; i < modifications.length; i++) {
|
|
1536
|
+
var mod = modifications[i];
|
|
1537
|
+
steps.push(outer.encodeMediaKey(mod.key));
|
|
1538
|
+
steps.push(codec.encodeUByte(mod.controlByte));
|
|
1539
|
+
if (!(mod.controlByte & 0x1) && !(mod.controlByte & 0x2)) {
|
|
1540
|
+
steps.push(codec.encodeBytes(mod.versionRead));
|
|
1541
|
+
}
|
|
1542
|
+
if (!(mod.controlByte & 0x4)) {
|
|
1543
|
+
steps.push(codec.encodeUByte(0x77));
|
|
1544
|
+
steps.push(outer.encodeMediaValue(mod.value));
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
return steps;
|
|
1548
|
+
};
|
|
1549
|
+
},
|
|
1550
|
+
encodeXidOnly: function(xid) {
|
|
1551
|
+
var outer = this;
|
|
1552
|
+
return function() {
|
|
1553
|
+
return outer.encodeXid(xid);
|
|
1554
|
+
};
|
|
1555
|
+
},
|
|
1556
|
+
decodeXaResponse: function() {
|
|
1557
|
+
return function(header, bytebuf) {
|
|
1558
|
+
var code = DECODE_INT(bytebuf);
|
|
1559
|
+
if (!f.existy(code)) return {continue: false};
|
|
1560
|
+
return {result: code, continue: true};
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1418
1565
|
/**
|
|
1419
1566
|
* Constructs a Hot Rod protocol instance for the given version.
|
|
1420
1567
|
* @param {number} v Protocol version number.
|
|
@@ -1503,13 +1650,7 @@
|
|
|
1503
1650
|
, Ping29Mixin
|
|
1504
1651
|
, ProtostreamType
|
|
1505
1652
|
, ProtobufRoot
|
|
1506
|
-
|
|
1507
|
-
// TODO 2.6 add listener change: listener event interests
|
|
1508
|
-
// TODO 2.7 new ops: prepare, commit and rollback
|
|
1509
|
-
// TODO 2.7 new ops: counter operations
|
|
1510
|
-
// TODO 2.7 new events: counter events
|
|
1511
|
-
// TODO 2.8 listener events: can come from any connection
|
|
1512
|
-
// TODO 2.8 header change: media types
|
|
1653
|
+
, TransactionMixin
|
|
1513
1654
|
);
|
|
1514
1655
|
|
|
1515
1656
|
_.extend(Protocol30.prototype
|
|
@@ -1525,6 +1666,7 @@
|
|
|
1525
1666
|
, Ping30Mixin
|
|
1526
1667
|
, ProtostreamType
|
|
1527
1668
|
, ProtobufRoot
|
|
1669
|
+
, TransactionMixin
|
|
1528
1670
|
);
|
|
1529
1671
|
|
|
1530
1672
|
_.extend(Protocol31.prototype
|
|
@@ -1539,9 +1681,10 @@
|
|
|
1539
1681
|
, SASLMixin
|
|
1540
1682
|
, Ping30Mixin
|
|
1541
1683
|
, CounterMixin
|
|
1684
|
+
, MultimapMixin
|
|
1542
1685
|
, ProtostreamType
|
|
1543
1686
|
, ProtobufRoot
|
|
1544
|
-
|
|
1687
|
+
, TransactionMixin
|
|
1545
1688
|
);
|
|
1546
1689
|
|
|
1547
1690
|
_.extend(Protocol40.prototype
|
|
@@ -1558,8 +1701,10 @@
|
|
|
1558
1701
|
, SASLMixin
|
|
1559
1702
|
, Ping30Mixin
|
|
1560
1703
|
, CounterMixin
|
|
1704
|
+
, MultimapMixin
|
|
1561
1705
|
, ProtostreamType
|
|
1562
1706
|
, ProtobufRoot
|
|
1707
|
+
, TransactionMixin
|
|
1563
1708
|
);
|
|
1564
1709
|
|
|
1565
1710
|
_.extend(Protocol41.prototype
|
|
@@ -1576,9 +1721,10 @@
|
|
|
1576
1721
|
, SASLMixin
|
|
1577
1722
|
, Ping30Mixin
|
|
1578
1723
|
, CounterMixin
|
|
1724
|
+
, MultimapMixin
|
|
1579
1725
|
, ProtostreamType
|
|
1580
1726
|
, ProtobufRoot
|
|
1581
|
-
|
|
1727
|
+
, TransactionMixin
|
|
1582
1728
|
);
|
|
1583
1729
|
|
|
1584
1730
|
exports.version22 = function(clientOpts) {
|
|
@@ -1613,6 +1759,8 @@
|
|
|
1613
1759
|
|
|
1614
1760
|
exports.VERSION_ORDER = VERSION_ORDER;
|
|
1615
1761
|
|
|
1762
|
+
exports.FLAGS = FLAGS;
|
|
1763
|
+
|
|
1616
1764
|
/**
|
|
1617
1765
|
* Creates a protocol instance for the given version string.
|
|
1618
1766
|
* @param {string} version Protocol version (e.g. '3.1', '4.0').
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
(function() {
|
|
4
|
+
|
|
5
|
+
var crypto = require('crypto');
|
|
6
|
+
|
|
7
|
+
var FORMAT_ID = 0x48525458;
|
|
8
|
+
var NOT_READ = 0x1, NON_EXISTING = 0x2, REMOVE_OP = 0x4;
|
|
9
|
+
var XA_OK = 0, XA_RDONLY = 3;
|
|
10
|
+
|
|
11
|
+
function generateXid() {
|
|
12
|
+
return {
|
|
13
|
+
formatId: FORMAT_ID,
|
|
14
|
+
globalTxId: crypto.randomBytes(16),
|
|
15
|
+
branchQualifier: crypto.randomBytes(16)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function TransactionContext() {
|
|
20
|
+
this.xid = generateXid();
|
|
21
|
+
// key (string) → { value, removed (bool), versionRead (Buffer|null), wasRead (bool), existed (bool) }
|
|
22
|
+
this.entries = new Map();
|
|
23
|
+
this.active = true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
TransactionContext.prototype.trackPut = function(key, value) {
|
|
27
|
+
var entry = this.entries.get(key);
|
|
28
|
+
if (entry) {
|
|
29
|
+
entry.value = value;
|
|
30
|
+
entry.removed = false;
|
|
31
|
+
} else {
|
|
32
|
+
this.entries.set(key, {
|
|
33
|
+
value: value,
|
|
34
|
+
removed: false,
|
|
35
|
+
versionRead: null,
|
|
36
|
+
wasRead: false,
|
|
37
|
+
existed: false
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
TransactionContext.prototype.trackRemove = function(key) {
|
|
43
|
+
var entry = this.entries.get(key);
|
|
44
|
+
if (entry) {
|
|
45
|
+
entry.removed = true;
|
|
46
|
+
entry.value = undefined;
|
|
47
|
+
} else {
|
|
48
|
+
this.entries.set(key, {
|
|
49
|
+
value: undefined,
|
|
50
|
+
removed: true,
|
|
51
|
+
versionRead: null,
|
|
52
|
+
wasRead: false,
|
|
53
|
+
existed: false
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
TransactionContext.prototype.trackRead = function(key, meta) {
|
|
59
|
+
var entry = this.entries.get(key);
|
|
60
|
+
var version = meta ? meta.version : null;
|
|
61
|
+
var existed = meta !== undefined;
|
|
62
|
+
if (entry) {
|
|
63
|
+
if (!entry.wasRead) {
|
|
64
|
+
entry.wasRead = true;
|
|
65
|
+
entry.versionRead = version;
|
|
66
|
+
entry.existed = existed;
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
this.entries.set(key, {
|
|
70
|
+
value: meta ? meta.value : undefined,
|
|
71
|
+
removed: false,
|
|
72
|
+
versionRead: version,
|
|
73
|
+
wasRead: true,
|
|
74
|
+
existed: existed
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
TransactionContext.prototype.getLocalValue = function(key) {
|
|
80
|
+
var entry = this.entries.get(key);
|
|
81
|
+
if (!entry) return { found: false };
|
|
82
|
+
if (entry.removed) return { found: true, value: undefined };
|
|
83
|
+
if (entry.value !== undefined) return { found: true, value: entry.value };
|
|
84
|
+
return { found: false };
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
TransactionContext.prototype.computeControlByte = function(entry) {
|
|
88
|
+
var control = 0;
|
|
89
|
+
if (!entry.wasRead) {
|
|
90
|
+
control |= NOT_READ;
|
|
91
|
+
} else if (!entry.existed) {
|
|
92
|
+
control |= NON_EXISTING;
|
|
93
|
+
}
|
|
94
|
+
if (entry.removed) {
|
|
95
|
+
control |= REMOVE_OP;
|
|
96
|
+
}
|
|
97
|
+
return control;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
TransactionContext.prototype.getModifications = function() {
|
|
101
|
+
var mods = [];
|
|
102
|
+
this.entries.forEach(function(entry, key) {
|
|
103
|
+
var control = this.computeControlByte(entry);
|
|
104
|
+
mods.push({
|
|
105
|
+
key: key,
|
|
106
|
+
controlByte: control,
|
|
107
|
+
versionRead: entry.versionRead,
|
|
108
|
+
value: entry.value
|
|
109
|
+
});
|
|
110
|
+
}.bind(this));
|
|
111
|
+
return mods;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
exports.TransactionContext = TransactionContext;
|
|
115
|
+
exports.generateXid = generateXid;
|
|
116
|
+
exports.FORMAT_ID = FORMAT_ID;
|
|
117
|
+
exports.NOT_READ = NOT_READ;
|
|
118
|
+
exports.NON_EXISTING = NON_EXISTING;
|
|
119
|
+
exports.REMOVE_OP = REMOVE_OP;
|
|
120
|
+
exports.XA_OK = XA_OK;
|
|
121
|
+
exports.XA_RDONLY = XA_RDONLY;
|
|
122
|
+
|
|
123
|
+
}.call(this));
|
package/lib/uri.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
(function() {
|
|
4
|
+
|
|
5
|
+
var _ = require('underscore');
|
|
6
|
+
|
|
7
|
+
var DEFAULT_PORT = 11222;
|
|
8
|
+
|
|
9
|
+
var QUERY_PARAMS = {
|
|
10
|
+
'sasl_mechanism': { path: ['authentication', 'saslMechanism'], type: 'string' },
|
|
11
|
+
'trust_store_file_name': { path: ['ssl', 'trustCerts'], type: 'string_array' },
|
|
12
|
+
'trust_ca': { path: ['ssl', 'trustCerts'], type: 'string_array' },
|
|
13
|
+
'key_store_file_name': { path: ['ssl', 'clientAuth', 'cert'], type: 'string' },
|
|
14
|
+
'client_cert': { path: ['ssl', 'clientAuth', 'cert'], type: 'string' },
|
|
15
|
+
'key_store_password': { path: ['ssl', 'clientAuth', 'key'], type: 'string' },
|
|
16
|
+
'client_key': { path: ['ssl', 'clientAuth', 'key'], type: 'string' },
|
|
17
|
+
'sni_host_name': { path: ['ssl', 'sniHostName'], type: 'string' },
|
|
18
|
+
'sni_host': { path: ['ssl', 'sniHostName'], type: 'string' },
|
|
19
|
+
'max_retries': { path: ['maxRetries'], type: 'int' },
|
|
20
|
+
'cache_name': { path: ['cacheName'], type: 'string' }
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Checks whether the argument is a Hot Rod URI string.
|
|
25
|
+
* @param {*} arg Value to check.
|
|
26
|
+
* @returns {boolean} True if the argument is a hotrod:// or hotrods:// URI.
|
|
27
|
+
*/
|
|
28
|
+
function isHotrodURI(arg) {
|
|
29
|
+
return _.isString(arg) &&
|
|
30
|
+
(arg.indexOf('hotrod://') === 0 || arg.indexOf('hotrods://') === 0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Splits a host string into host and port, handling IPv6 bracket notation.
|
|
35
|
+
* @param {string} hostStr Host string like "host:port" or "[::1]:port".
|
|
36
|
+
* @returns {{host: string, port: number}} Parsed host and port.
|
|
37
|
+
*/
|
|
38
|
+
function splitHostPort(hostStr) {
|
|
39
|
+
hostStr = hostStr.trim();
|
|
40
|
+
if (hostStr.length === 0) {
|
|
41
|
+
throw new Error('Empty host in URI');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// IPv6 bracket notation: [::1]:port
|
|
45
|
+
if (hostStr.charAt(0) === '[') {
|
|
46
|
+
var closeBracket = hostStr.indexOf(']');
|
|
47
|
+
if (closeBracket === -1) {
|
|
48
|
+
throw new Error(`Invalid IPv6 address, missing closing bracket: ${hostStr}`);
|
|
49
|
+
}
|
|
50
|
+
var ipv6Host = hostStr.substring(1, closeBracket);
|
|
51
|
+
var afterBracket = hostStr.substring(closeBracket + 1);
|
|
52
|
+
if (afterBracket.length === 0) {
|
|
53
|
+
return { host: ipv6Host, port: DEFAULT_PORT };
|
|
54
|
+
}
|
|
55
|
+
if (afterBracket.charAt(0) === ':') {
|
|
56
|
+
var ipv6Port = parseInt(afterBracket.substring(1), 10);
|
|
57
|
+
if (isNaN(ipv6Port)) {
|
|
58
|
+
throw new Error(`Invalid port in URI: ${afterBracket.substring(1)}`);
|
|
59
|
+
}
|
|
60
|
+
return { host: ipv6Host, port: ipv6Port };
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Invalid IPv6 host format: ${hostStr}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
var lastColon = hostStr.lastIndexOf(':');
|
|
66
|
+
if (lastColon === -1) {
|
|
67
|
+
return { host: hostStr, port: DEFAULT_PORT };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var host = hostStr.substring(0, lastColon);
|
|
71
|
+
var portStr = hostStr.substring(lastColon + 1);
|
|
72
|
+
var port = parseInt(portStr, 10);
|
|
73
|
+
if (isNaN(port)) {
|
|
74
|
+
throw new Error(`Invalid port in URI: ${portStr}`);
|
|
75
|
+
}
|
|
76
|
+
return { host: host, port: port };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Sets a nested value in an object given a path array.
|
|
81
|
+
* @param {Object} obj Target object.
|
|
82
|
+
* @param {string[]} path Array of keys.
|
|
83
|
+
* @param {*} value Value to set.
|
|
84
|
+
* @returns {void}
|
|
85
|
+
*/
|
|
86
|
+
function setNested(obj, path, value) {
|
|
87
|
+
var current = obj;
|
|
88
|
+
for (var i = 0; i < path.length - 1; i++) {
|
|
89
|
+
if (!current[path[i]] || !_.isObject(current[path[i]])) {
|
|
90
|
+
current[path[i]] = {};
|
|
91
|
+
}
|
|
92
|
+
current = current[path[i]];
|
|
93
|
+
}
|
|
94
|
+
current[path[path.length - 1]] = value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Parses a Hot Rod URI string into servers and options.
|
|
99
|
+
* @param {string} uriString URI in the format hotrod://[user:pass@]host1[:port1][,host2[:port2]][?params].
|
|
100
|
+
* @returns {{servers: Array<{host: string, port: number}>, options: Object}} Parsed result.
|
|
101
|
+
*/
|
|
102
|
+
function parseHotrodURI(uriString) {
|
|
103
|
+
var tls = false;
|
|
104
|
+
var rest;
|
|
105
|
+
|
|
106
|
+
if (uriString.indexOf('hotrods://') === 0) {
|
|
107
|
+
tls = true;
|
|
108
|
+
rest = uriString.substring('hotrods://'.length);
|
|
109
|
+
} else if (uriString.indexOf('hotrod://') === 0) {
|
|
110
|
+
rest = uriString.substring('hotrod://'.length);
|
|
111
|
+
} else {
|
|
112
|
+
throw new Error('Invalid URI scheme, expected hotrod:// or hotrods://');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Split authority from query string
|
|
116
|
+
var queryString = '';
|
|
117
|
+
var qIndex = rest.indexOf('?');
|
|
118
|
+
var authority;
|
|
119
|
+
if (qIndex !== -1) {
|
|
120
|
+
authority = rest.substring(0, qIndex);
|
|
121
|
+
queryString = rest.substring(qIndex + 1);
|
|
122
|
+
} else {
|
|
123
|
+
authority = rest;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Split userinfo from hosts (find last @ to handle passwords with @)
|
|
127
|
+
var userName, password;
|
|
128
|
+
var atIndex = authority.lastIndexOf('@');
|
|
129
|
+
var hostsStr;
|
|
130
|
+
if (atIndex !== -1) {
|
|
131
|
+
var userinfo = authority.substring(0, atIndex);
|
|
132
|
+
hostsStr = authority.substring(atIndex + 1);
|
|
133
|
+
var colonIndex = userinfo.indexOf(':');
|
|
134
|
+
if (colonIndex !== -1) {
|
|
135
|
+
userName = decodeURIComponent(userinfo.substring(0, colonIndex));
|
|
136
|
+
password = decodeURIComponent(userinfo.substring(colonIndex + 1));
|
|
137
|
+
} else {
|
|
138
|
+
userName = decodeURIComponent(userinfo);
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
hostsStr = authority;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (hostsStr.length === 0) {
|
|
145
|
+
throw new Error('No host specified in URI');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Parse comma-separated hosts
|
|
149
|
+
var hostParts = hostsStr.split(',');
|
|
150
|
+
var servers = [];
|
|
151
|
+
for (var i = 0; i < hostParts.length; i++) {
|
|
152
|
+
if (hostParts[i].length > 0) {
|
|
153
|
+
servers.push(splitHostPort(hostParts[i]));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (servers.length === 0) {
|
|
157
|
+
throw new Error('No host specified in URI');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Build options from URI components
|
|
161
|
+
var options = {};
|
|
162
|
+
|
|
163
|
+
if (tls) {
|
|
164
|
+
setNested(options, ['ssl', 'enabled'], true);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (userName !== undefined) {
|
|
168
|
+
setNested(options, ['authentication', 'enabled'], true);
|
|
169
|
+
setNested(options, ['authentication', 'userName'], userName);
|
|
170
|
+
if (password !== undefined) {
|
|
171
|
+
setNested(options, ['authentication', 'password'], password);
|
|
172
|
+
}
|
|
173
|
+
setNested(options, ['authentication', 'saslMechanism'], 'PLAIN');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Parse query parameters
|
|
177
|
+
if (queryString.length > 0) {
|
|
178
|
+
var params = new URLSearchParams(queryString);
|
|
179
|
+
params.forEach(function(value, key) {
|
|
180
|
+
var mapping = QUERY_PARAMS[key];
|
|
181
|
+
if (!mapping) {
|
|
182
|
+
throw new Error(`Unknown URI parameter: ${key}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
var parsed;
|
|
186
|
+
if (mapping.type === 'int') {
|
|
187
|
+
parsed = parseInt(value, 10);
|
|
188
|
+
if (isNaN(parsed)) {
|
|
189
|
+
throw new Error(`Invalid integer value for ${key}: ${value}`);
|
|
190
|
+
}
|
|
191
|
+
} else if (mapping.type === 'string_array') {
|
|
192
|
+
parsed = [value];
|
|
193
|
+
} else {
|
|
194
|
+
parsed = value;
|
|
195
|
+
}
|
|
196
|
+
setNested(options, mapping.path, parsed);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return { servers: servers, options: options };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
exports.isHotrodURI = isHotrodURI;
|
|
204
|
+
exports.parseHotrodURI = parseHotrodURI;
|
|
205
|
+
|
|
206
|
+
}.call(this));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infinispan",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Infinispan Javascript client",
|
|
5
5
|
"main": "index",
|
|
6
6
|
"typings": "./types",
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"lint": "eslint --ignore-path .gitignore lib spec index.js",
|
|
12
|
-
"test": "
|
|
12
|
+
"test": "node scripts/docker-test.js",
|
|
13
|
+
"docker:up": "docker compose -p ispn-test up -d --wait && docker compose -p ispn-test --profile failover create server-failover-one server-failover-two server-failover-three",
|
|
14
|
+
"docker:down": "docker compose -p ispn-test --profile failover down --remove-orphans",
|
|
15
|
+
"ssl:generate": "node scripts/make-ssl.js",
|
|
16
|
+
"docs:api": "jsdoc lib/*.js",
|
|
17
|
+
"docs:user": "node scripts/gen-asciidoc.js"
|
|
13
18
|
},
|
|
14
19
|
"author": "Infinispan Community",
|
|
15
20
|
"license": "Apache-2.0",
|
|
@@ -28,7 +33,6 @@
|
|
|
28
33
|
},
|
|
29
34
|
"dependencies": {
|
|
30
35
|
"buffer-xor": "^2.0.2",
|
|
31
|
-
"jsdoc": "^4.0.2",
|
|
32
36
|
"log4js": "^6.4.6",
|
|
33
37
|
"protobufjs": "^7.0.0",
|
|
34
38
|
"underscore": "^1.13.3",
|
|
@@ -37,6 +41,7 @@
|
|
|
37
41
|
"devDependencies": {
|
|
38
42
|
"eslint": "^8.26.0",
|
|
39
43
|
"jasmine": "^6.1.0",
|
|
44
|
+
"jsdoc": "^4.0.2",
|
|
40
45
|
"long": "^5.2.3"
|
|
41
46
|
}
|
|
42
47
|
}
|
package/server/.keep
ADDED
|
File without changes
|