infinispan 0.15.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/SECURITY.md +27 -0
- package/lib/codec.js +12 -0
- package/lib/infinispan.js +210 -0
- package/lib/protocols.js +148 -13
- package/lib/transaction.js +123 -0
- package/package.json +2 -3
package/SECURITY.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
The Infinispan team and community take all security bugs very seriously.
|
|
4
|
+
You can find our guidelines here regarding our policy and security disclosure.
|
|
5
|
+
|
|
6
|
+
## Reporting security issues
|
|
7
|
+
|
|
8
|
+
Please report any security issues you find in Infinispan to:
|
|
9
|
+
|
|
10
|
+
security at infinispan.org
|
|
11
|
+
|
|
12
|
+
### Why follow this process
|
|
13
|
+
|
|
14
|
+
Due to the sensitive nature of security bugs, the disclosure process is more constrained than a regular bug.
|
|
15
|
+
We appreciate you following these industry accepted guidelines, which gives time for a proper fix and limit the time
|
|
16
|
+
window of attack.
|
|
17
|
+
|
|
18
|
+
## Supported Versions
|
|
19
|
+
|
|
20
|
+
Use this section to tell people about which versions of your project are
|
|
21
|
+
currently being supported with security updates.
|
|
22
|
+
|
|
23
|
+
| Version | Supported |
|
|
24
|
+
| -------- | ------------------ |
|
|
25
|
+
| 0.14.0 | :white_check_mark: |
|
|
26
|
+
| < 0.14.0 | :x: |
|
|
27
|
+
|
package/lib/codec.js
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
exports.decodeSignedInt = f.lift(doDecodeSignedInt, _.identity);
|
|
37
37
|
exports.decodeVariableBytes = f.lift(doDecodeVariableBytes, _.identity);
|
|
38
38
|
exports.decodeShort = f.lift(doDecodeShort, _.identity);
|
|
39
|
+
exports.decodeInt = f.lift(doDecodeInt, _.identity);
|
|
39
40
|
exports.decodeProtobuf = f.lift(doDecodeProtobuf, _.identity);
|
|
40
41
|
exports.decodeQuery = f.lift(doDecodeQuery,_.identity);
|
|
41
42
|
|
|
@@ -560,6 +561,17 @@
|
|
|
560
561
|
return uncheckedReadShort(bytebuf)();
|
|
561
562
|
}
|
|
562
563
|
|
|
564
|
+
function doDecodeInt(bytebuf) {
|
|
565
|
+
if (4 > bytebuf.buf.length - bytebuf.offset) {
|
|
566
|
+
logger.tracef('Can not fully read 4 bytes (buffer size is %d, buffer offset %d)',
|
|
567
|
+
bytebuf.buf.length, bytebuf.offset);
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
var val = bytebuf.buf.readInt32BE(bytebuf.offset);
|
|
571
|
+
bytebuf.offset += 4;
|
|
572
|
+
return val;
|
|
573
|
+
}
|
|
574
|
+
|
|
563
575
|
/**
|
|
564
576
|
* Decode a Protobuf WrappedMessage from the buffer and unwrap it.
|
|
565
577
|
* @param {Object} bytebuf - The byte buffer to read from.
|
package/lib/infinispan.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
var io = require('./io');
|
|
19
19
|
var listeners = require('./listeners');
|
|
20
20
|
var nearCacheFactory = require('./near-cache');
|
|
21
|
+
var tx = require('./transaction');
|
|
21
22
|
|
|
22
23
|
var Client = function(addrs, clientOpts) {
|
|
23
24
|
var logger = u.logger('client');
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
var p = protocolResolver(clientOpts['version']);
|
|
40
41
|
var listen = listeners(p);
|
|
41
42
|
var nc = clientOpts.nearCache ? nearCacheFactory(clientOpts.nearCache.maxEntries) : null;
|
|
43
|
+
var txCtx = null;
|
|
42
44
|
|
|
43
45
|
var TINY = 16, SMALL = 32, MEDIUM = 64, BIG = 128;
|
|
44
46
|
|
|
@@ -327,6 +329,17 @@
|
|
|
327
329
|
* @since 0.3
|
|
328
330
|
*/
|
|
329
331
|
get: function(k) {
|
|
332
|
+
if (txCtx) {
|
|
333
|
+
var local = txCtx.getLocalValue(k);
|
|
334
|
+
if (local.found) return Promise.resolve(local.value);
|
|
335
|
+
var ctx = transport.context(SMALL);
|
|
336
|
+
logger.debugf('Invoke transactional get(msgId=%d,key=%s)', ctx.id, u.str(k));
|
|
337
|
+
var decoder = p.decodeWithMeta();
|
|
338
|
+
return futureKey(ctx, 0x1B, k, p.encodeKey(k), decoder).then(function(meta) {
|
|
339
|
+
txCtx.trackRead(k, meta);
|
|
340
|
+
return meta ? meta.value : undefined;
|
|
341
|
+
});
|
|
342
|
+
}
|
|
330
343
|
if (nc) {
|
|
331
344
|
var cached = nc.get(k);
|
|
332
345
|
if (cached !== undefined) {
|
|
@@ -439,6 +452,8 @@
|
|
|
439
452
|
* Lifespan for the stored entry.
|
|
440
453
|
* @property {DurationUnit} maxIdle -
|
|
441
454
|
* Max idle time for the stored entry.
|
|
455
|
+
* @property {Number} flags - Bitwise OR of Hot Rod flags
|
|
456
|
+
* (e.g. infinispan.flags.SKIP_CACHE_LOAD | infinispan.flags.SKIP_INDEXING).
|
|
442
457
|
* @since 0.3
|
|
443
458
|
*/
|
|
444
459
|
/**
|
|
@@ -455,6 +470,11 @@
|
|
|
455
470
|
* @since 0.3
|
|
456
471
|
*/
|
|
457
472
|
put: function(k, v, opts) {
|
|
473
|
+
if (txCtx) {
|
|
474
|
+
logger.debugf('Transactional put(key=%s)', u.str(k));
|
|
475
|
+
txCtx.trackPut(k, v);
|
|
476
|
+
return Promise.resolve(undefined);
|
|
477
|
+
}
|
|
458
478
|
if (nc) nc.remove(k);
|
|
459
479
|
var ctx = transport.context(MEDIUM);
|
|
460
480
|
logger.debugl(function() { return ['Invoke put(msgId=%d,key=%s,value=%s,opts=%s)',
|
|
@@ -486,6 +506,11 @@
|
|
|
486
506
|
* @since 0.3
|
|
487
507
|
*/
|
|
488
508
|
remove: function(k, opts) {
|
|
509
|
+
if (txCtx) {
|
|
510
|
+
logger.debugf('Transactional remove(key=%s)', u.str(k));
|
|
511
|
+
txCtx.trackRemove(k);
|
|
512
|
+
return Promise.resolve(true);
|
|
513
|
+
}
|
|
489
514
|
if (nc) nc.remove(k);
|
|
490
515
|
var ctx = transport.context(SMALL);
|
|
491
516
|
logger.debugl(function() {return ['Invoke remove(msgId=%d,key=%s,opts=%s)',
|
|
@@ -1073,6 +1098,122 @@
|
|
|
1073
1098
|
logger.debugf('Invoke counterGetAndSet(msgId=%d,name=%s,value=%d)', ctx.id, name, value);
|
|
1074
1099
|
return future(ctx, 0x7F, p.encodeCounterNameValue(name, value), p.decodeCounterValue);
|
|
1075
1100
|
},
|
|
1101
|
+
/**
|
|
1102
|
+
* Get all values associated with a key in the multimap.
|
|
1103
|
+
*
|
|
1104
|
+
* @param {(String|Object)} k Key to retrieve.
|
|
1105
|
+
* @returns {Promise.<Array>}
|
|
1106
|
+
* A promise that will be completed with an array of values,
|
|
1107
|
+
* or an empty array if the key does not exist.
|
|
1108
|
+
* @memberof Client#
|
|
1109
|
+
* @since 0.16
|
|
1110
|
+
*/
|
|
1111
|
+
multimapGet: function(k) {
|
|
1112
|
+
var ctx = transport.context(SMALL);
|
|
1113
|
+
logger.debugf('Invoke multimapGet(msgId=%d,key=%s)', ctx.id, u.str(k));
|
|
1114
|
+
return futureKey(ctx, 0x67, k, p.encodeMultimapKey(k), p.decodeMultimapCollection());
|
|
1115
|
+
},
|
|
1116
|
+
/**
|
|
1117
|
+
* Add a value to the collection associated with a key in the multimap.
|
|
1118
|
+
*
|
|
1119
|
+
* @param {(String|Object)} k Key.
|
|
1120
|
+
* @param {(String|Object)} v Value to add.
|
|
1121
|
+
* @param {Object=} opts Optional store options (lifespan, maxIdle).
|
|
1122
|
+
* @returns {Promise}
|
|
1123
|
+
* A promise that will be completed when the value has been added.
|
|
1124
|
+
* @memberof Client#
|
|
1125
|
+
* @since 0.16
|
|
1126
|
+
*/
|
|
1127
|
+
multimapPut: function(k, v, opts) {
|
|
1128
|
+
var ctx = transport.context(MEDIUM);
|
|
1129
|
+
logger.debugf('Invoke multimapPut(msgId=%d,key=%s,value=%s)', ctx.id, u.str(k), u.str(v));
|
|
1130
|
+
return futureKey(ctx, 0x6B, k, p.encodeMultimapPut(k, v), p.complete(_.constant(undefined)), opts);
|
|
1131
|
+
},
|
|
1132
|
+
/**
|
|
1133
|
+
* Remove all values associated with a key in the multimap.
|
|
1134
|
+
*
|
|
1135
|
+
* @param {(String|Object)} k Key to remove.
|
|
1136
|
+
* @returns {Promise.<Boolean>}
|
|
1137
|
+
* A promise that will be completed with true if the key existed.
|
|
1138
|
+
* @memberof Client#
|
|
1139
|
+
* @since 0.16
|
|
1140
|
+
*/
|
|
1141
|
+
multimapRemoveKey: function(k) {
|
|
1142
|
+
var ctx = transport.context(SMALL);
|
|
1143
|
+
logger.debugf('Invoke multimapRemoveKey(msgId=%d,key=%s)', ctx.id, u.str(k));
|
|
1144
|
+
return futureKey(ctx, 0x6D, k, p.encodeMultimapKey(k), p.decodeMultimapBoolean);
|
|
1145
|
+
},
|
|
1146
|
+
/**
|
|
1147
|
+
* Remove a specific value from the collection associated with a key.
|
|
1148
|
+
*
|
|
1149
|
+
* @param {(String|Object)} k Key.
|
|
1150
|
+
* @param {(String|Object)} v Value to remove.
|
|
1151
|
+
* @returns {Promise.<Boolean>}
|
|
1152
|
+
* A promise that will be completed with true if the entry existed.
|
|
1153
|
+
* @memberof Client#
|
|
1154
|
+
* @since 0.16
|
|
1155
|
+
*/
|
|
1156
|
+
multimapRemoveEntry: function(k, v) {
|
|
1157
|
+
var ctx = transport.context(SMALL);
|
|
1158
|
+
logger.debugf('Invoke multimapRemoveEntry(msgId=%d,key=%s,value=%s)', ctx.id, u.str(k), u.str(v));
|
|
1159
|
+
return futureKey(ctx, 0x6F, k, p.encodeMultimapKeyValue(k, v), p.decodeMultimapBoolean);
|
|
1160
|
+
},
|
|
1161
|
+
/**
|
|
1162
|
+
* Get the total number of key-value pairs in the multimap.
|
|
1163
|
+
*
|
|
1164
|
+
* @returns {Promise.<Number>}
|
|
1165
|
+
* A promise that will be completed with the total size.
|
|
1166
|
+
* @memberof Client#
|
|
1167
|
+
* @since 0.16
|
|
1168
|
+
*/
|
|
1169
|
+
multimapSize: function() {
|
|
1170
|
+
var ctx = transport.context(SMALL);
|
|
1171
|
+
logger.debugf('Invoke multimapSize(msgId=%d)', ctx.id);
|
|
1172
|
+
return future(ctx, 0x71, p.encodeMultimapSupportsDuplicates(), p.decodeMultimapSize);
|
|
1173
|
+
},
|
|
1174
|
+
/**
|
|
1175
|
+
* Check whether a specific key-value pair exists in the multimap.
|
|
1176
|
+
*
|
|
1177
|
+
* @param {(String|Object)} k Key.
|
|
1178
|
+
* @param {(String|Object)} v Value.
|
|
1179
|
+
* @returns {Promise.<Boolean>}
|
|
1180
|
+
* A promise that will be completed with true if the entry exists.
|
|
1181
|
+
* @memberof Client#
|
|
1182
|
+
* @since 0.16
|
|
1183
|
+
*/
|
|
1184
|
+
multimapContainsEntry: function(k, v) {
|
|
1185
|
+
var ctx = transport.context(SMALL);
|
|
1186
|
+
logger.debugf('Invoke multimapContainsEntry(msgId=%d,key=%s,value=%s)', ctx.id, u.str(k), u.str(v));
|
|
1187
|
+
return futureKey(ctx, 0x73, k, p.encodeMultimapKeyValue(k, v), p.decodeMultimapBoolean);
|
|
1188
|
+
},
|
|
1189
|
+
/**
|
|
1190
|
+
* Check whether a key exists in the multimap.
|
|
1191
|
+
*
|
|
1192
|
+
* @param {(String|Object)} k Key to check.
|
|
1193
|
+
* @returns {Promise.<Boolean>}
|
|
1194
|
+
* A promise that will be completed with true if the key exists.
|
|
1195
|
+
* @memberof Client#
|
|
1196
|
+
* @since 0.16
|
|
1197
|
+
*/
|
|
1198
|
+
multimapContainsKey: function(k) {
|
|
1199
|
+
var ctx = transport.context(SMALL);
|
|
1200
|
+
logger.debugf('Invoke multimapContainsKey(msgId=%d,key=%s)', ctx.id, u.str(k));
|
|
1201
|
+
return futureKey(ctx, 0x75, k, p.encodeMultimapKey(k), p.decodeMultimapBoolean);
|
|
1202
|
+
},
|
|
1203
|
+
/**
|
|
1204
|
+
* Check whether any key contains the given value in the multimap.
|
|
1205
|
+
*
|
|
1206
|
+
* @param {(String|Object)} v Value to check.
|
|
1207
|
+
* @returns {Promise.<Boolean>}
|
|
1208
|
+
* A promise that will be completed with true if any key contains the value.
|
|
1209
|
+
* @memberof Client#
|
|
1210
|
+
* @since 0.16
|
|
1211
|
+
*/
|
|
1212
|
+
multimapContainsValue: function(v) {
|
|
1213
|
+
var ctx = transport.context(SMALL);
|
|
1214
|
+
logger.debugf('Invoke multimapContainsValue(msgId=%d,value=%s)', ctx.id, u.str(v));
|
|
1215
|
+
return future(ctx, 0x77, p.encodeMultimapValue(v), p.decodeMultimapBoolean);
|
|
1216
|
+
},
|
|
1076
1217
|
/**
|
|
1077
1218
|
* Get server topology related information.
|
|
1078
1219
|
*
|
|
@@ -1261,6 +1402,73 @@
|
|
|
1261
1402
|
logger.debugf('Invoke admin.removeSchema(msgId=%d,name=%s)', ctx.id, name);
|
|
1262
1403
|
return future(ctx, 0x2B, p.encodeNameParams('@@schemas@delete', {name: name}), p.decodeValue());
|
|
1263
1404
|
}
|
|
1405
|
+
},
|
|
1406
|
+
/**
|
|
1407
|
+
* Get the transaction manager for this client.
|
|
1408
|
+
* Transactions buffer put/remove operations locally and send them
|
|
1409
|
+
* to the server atomically on commit via PREPARE_TX_2.
|
|
1410
|
+
* During an active transaction, get() reads local writes first,
|
|
1411
|
+
* falling back to getWithMetadata to capture entry versions for
|
|
1412
|
+
* conflict detection.
|
|
1413
|
+
*
|
|
1414
|
+
* @returns {Object} Transaction manager with begin(), commit(), and rollback() methods.
|
|
1415
|
+
* @memberof Client#
|
|
1416
|
+
* @since 0.16
|
|
1417
|
+
*/
|
|
1418
|
+
getTransactionManager: function() {
|
|
1419
|
+
return {
|
|
1420
|
+
begin: function() {
|
|
1421
|
+
if (txCtx) throw new Error('Transaction already active');
|
|
1422
|
+
txCtx = new tx.TransactionContext();
|
|
1423
|
+
logger.debugf('Transaction started (xid=%s)',
|
|
1424
|
+
txCtx.xid.globalTxId.toString('hex'));
|
|
1425
|
+
return Promise.resolve();
|
|
1426
|
+
},
|
|
1427
|
+
commit: function() {
|
|
1428
|
+
if (!txCtx) throw new Error('No active transaction');
|
|
1429
|
+
var xid = txCtx.xid;
|
|
1430
|
+
var mods = txCtx.getModifications();
|
|
1431
|
+
if (mods.length === 0) {
|
|
1432
|
+
txCtx = null;
|
|
1433
|
+
return Promise.resolve();
|
|
1434
|
+
}
|
|
1435
|
+
var ctx = transport.context(BIG);
|
|
1436
|
+
logger.debugf('Invoke prepareTx(msgId=%d, mods=%d)', ctx.id, mods.length);
|
|
1437
|
+
var decoder = p.decodeXaResponse();
|
|
1438
|
+
return future(ctx, 0x7D, p.encodePrepare(xid, mods, true, 60000), decoder)
|
|
1439
|
+
.then(function(xaCode) {
|
|
1440
|
+
txCtx = null;
|
|
1441
|
+
if (xaCode !== tx.XA_OK && xaCode !== tx.XA_RDONLY) {
|
|
1442
|
+
throw new Error(`Transaction prepare failed: XA code ${xaCode}`);
|
|
1443
|
+
}
|
|
1444
|
+
})
|
|
1445
|
+
.catch(function(err) {
|
|
1446
|
+
txCtx = null;
|
|
1447
|
+
throw err;
|
|
1448
|
+
});
|
|
1449
|
+
},
|
|
1450
|
+
rollback: function() {
|
|
1451
|
+
if (!txCtx) throw new Error('No active transaction');
|
|
1452
|
+
var xid = txCtx.xid;
|
|
1453
|
+
var mods = txCtx.getModifications();
|
|
1454
|
+
if (mods.length === 0) {
|
|
1455
|
+
txCtx = null;
|
|
1456
|
+
return Promise.resolve();
|
|
1457
|
+
}
|
|
1458
|
+
var ctx = transport.context(SMALL);
|
|
1459
|
+
logger.debugf('Invoke rollbackTx(msgId=%d)', ctx.id);
|
|
1460
|
+
var decoder = p.decodeXaResponse();
|
|
1461
|
+
return future(ctx, 0x3F, p.encodeXidOnly(xid), decoder)
|
|
1462
|
+
.then(function() { txCtx = null; })
|
|
1463
|
+
.catch(function(err) {
|
|
1464
|
+
txCtx = null;
|
|
1465
|
+
throw err;
|
|
1466
|
+
});
|
|
1467
|
+
},
|
|
1468
|
+
isActive: function() {
|
|
1469
|
+
return txCtx !== null;
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1264
1472
|
}
|
|
1265
1473
|
};
|
|
1266
1474
|
};
|
|
@@ -1390,6 +1598,8 @@
|
|
|
1390
1598
|
* @returns {Promise<ReturnType<Client>>} A promise that resolves to a connected client.
|
|
1391
1599
|
* @since 0.3
|
|
1392
1600
|
*/
|
|
1601
|
+
exports.flags = protocols.FLAGS;
|
|
1602
|
+
|
|
1393
1603
|
exports.client = function client(args, options) {
|
|
1394
1604
|
var addrs, uriOpts;
|
|
1395
1605
|
if (uri.isHotrodURI(args)) {
|
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;
|
|
@@ -1323,6 +1334,81 @@
|
|
|
1323
1334
|
};
|
|
1324
1335
|
}());
|
|
1325
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
|
+
|
|
1326
1412
|
/**
|
|
1327
1413
|
* Protocol 4.0+ requires an 'otherParams' map after media types in the header.
|
|
1328
1414
|
* This mixin overrides stepsHeader to append the count (0 = no params).
|
|
@@ -1428,6 +1514,54 @@
|
|
|
1428
1514
|
}());
|
|
1429
1515
|
|
|
1430
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
|
+
|
|
1431
1565
|
/**
|
|
1432
1566
|
* Constructs a Hot Rod protocol instance for the given version.
|
|
1433
1567
|
* @param {number} v Protocol version number.
|
|
@@ -1516,13 +1650,7 @@
|
|
|
1516
1650
|
, Ping29Mixin
|
|
1517
1651
|
, ProtostreamType
|
|
1518
1652
|
, ProtobufRoot
|
|
1519
|
-
|
|
1520
|
-
// TODO 2.6 add listener change: listener event interests
|
|
1521
|
-
// TODO 2.7 new ops: prepare, commit and rollback
|
|
1522
|
-
// TODO 2.7 new ops: counter operations
|
|
1523
|
-
// TODO 2.7 new events: counter events
|
|
1524
|
-
// TODO 2.8 listener events: can come from any connection
|
|
1525
|
-
// TODO 2.8 header change: media types
|
|
1653
|
+
, TransactionMixin
|
|
1526
1654
|
);
|
|
1527
1655
|
|
|
1528
1656
|
_.extend(Protocol30.prototype
|
|
@@ -1538,6 +1666,7 @@
|
|
|
1538
1666
|
, Ping30Mixin
|
|
1539
1667
|
, ProtostreamType
|
|
1540
1668
|
, ProtobufRoot
|
|
1669
|
+
, TransactionMixin
|
|
1541
1670
|
);
|
|
1542
1671
|
|
|
1543
1672
|
_.extend(Protocol31.prototype
|
|
@@ -1552,9 +1681,10 @@
|
|
|
1552
1681
|
, SASLMixin
|
|
1553
1682
|
, Ping30Mixin
|
|
1554
1683
|
, CounterMixin
|
|
1684
|
+
, MultimapMixin
|
|
1555
1685
|
, ProtostreamType
|
|
1556
1686
|
, ProtobufRoot
|
|
1557
|
-
|
|
1687
|
+
, TransactionMixin
|
|
1558
1688
|
);
|
|
1559
1689
|
|
|
1560
1690
|
_.extend(Protocol40.prototype
|
|
@@ -1571,8 +1701,10 @@
|
|
|
1571
1701
|
, SASLMixin
|
|
1572
1702
|
, Ping30Mixin
|
|
1573
1703
|
, CounterMixin
|
|
1704
|
+
, MultimapMixin
|
|
1574
1705
|
, ProtostreamType
|
|
1575
1706
|
, ProtobufRoot
|
|
1707
|
+
, TransactionMixin
|
|
1576
1708
|
);
|
|
1577
1709
|
|
|
1578
1710
|
_.extend(Protocol41.prototype
|
|
@@ -1589,9 +1721,10 @@
|
|
|
1589
1721
|
, SASLMixin
|
|
1590
1722
|
, Ping30Mixin
|
|
1591
1723
|
, CounterMixin
|
|
1724
|
+
, MultimapMixin
|
|
1592
1725
|
, ProtostreamType
|
|
1593
1726
|
, ProtobufRoot
|
|
1594
|
-
|
|
1727
|
+
, TransactionMixin
|
|
1595
1728
|
);
|
|
1596
1729
|
|
|
1597
1730
|
exports.version22 = function(clientOpts) {
|
|
@@ -1626,6 +1759,8 @@
|
|
|
1626
1759
|
|
|
1627
1760
|
exports.VERSION_ORDER = VERSION_ORDER;
|
|
1628
1761
|
|
|
1762
|
+
exports.FLAGS = FLAGS;
|
|
1763
|
+
|
|
1629
1764
|
/**
|
|
1630
1765
|
* Creates a protocol instance for the given version string.
|
|
1631
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/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,8 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"lint": "eslint --ignore-path .gitignore lib spec index.js",
|
|
12
|
-
"test": "
|
|
13
|
-
"test:docker": "node scripts/docker-test.js",
|
|
12
|
+
"test": "node scripts/docker-test.js",
|
|
14
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",
|
|
15
14
|
"docker:down": "docker compose -p ispn-test --profile failover down --remove-orphans",
|
|
16
15
|
"ssl:generate": "node scripts/make-ssl.js",
|