infinispan 0.13.0 → 0.15.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 +52 -80
- package/lib/codec.js +396 -28
- package/lib/functional.js +65 -7
- package/lib/infinispan.js +591 -69
- package/lib/io.js +402 -44
- package/lib/listeners.js +166 -5
- package/lib/near-cache.js +99 -0
- package/lib/protocols.js +580 -82
- package/lib/sasl/digest.js +22 -17
- package/lib/sasl/external.js +7 -4
- package/lib/sasl/factory.js +6 -5
- package/lib/sasl/oauthbearer.js +7 -5
- package/lib/sasl/plain.js +6 -4
- package/lib/sasl/scram.js +19 -11
- package/lib/uri.js +206 -0
- package/lib/utils.js +89 -24
- package/package.json +10 -4
- package/server/.keep +0 -0
- package/types/index.d.ts +236 -1
package/lib/sasl/digest.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* References:
|
|
17
17
|
* - [RFC 2831](http://tools.ietf.org/html/rfc2831)
|
|
18
18
|
*
|
|
19
|
+
* @param {Object} options Configuration options.
|
|
20
|
+
* @returns {void}
|
|
19
21
|
* @api public
|
|
20
22
|
*/
|
|
21
23
|
function Mechanism(options) {
|
|
@@ -36,7 +38,8 @@
|
|
|
36
38
|
* - `serviceType`
|
|
37
39
|
* - `authzid` authorization identity (optional)
|
|
38
40
|
*
|
|
39
|
-
* @param {Object} cred
|
|
41
|
+
* @param {Object} cred Credentials containing username, password, host, serviceType, and optional authzid.
|
|
42
|
+
* @returns {String} The encoded DIGEST response string.
|
|
40
43
|
* @api public
|
|
41
44
|
*/
|
|
42
45
|
Mechanism.prototype.response = function (cred) {
|
|
@@ -56,9 +59,9 @@
|
|
|
56
59
|
// the fact that TLS has largely superseded this functionality,
|
|
57
60
|
// implementing it is a low priority.
|
|
58
61
|
|
|
59
|
-
var uri = cred.serviceType
|
|
62
|
+
var uri = `${cred.serviceType }/${ cred.host}`;
|
|
60
63
|
if (cred.serviceName && cred.host !== cred.serviceName) {
|
|
61
|
-
uri +=
|
|
64
|
+
uri += `/${ serviceName}`;
|
|
62
65
|
}
|
|
63
66
|
var realm = cred.realm || this._realm || ''
|
|
64
67
|
, cnonce = this._genNonce()
|
|
@@ -68,13 +71,13 @@
|
|
|
68
71
|
, ha2
|
|
69
72
|
, digest;
|
|
70
73
|
var str = '';
|
|
71
|
-
str +=
|
|
72
|
-
if (realm) { str +=
|
|
73
|
-
str +=
|
|
74
|
-
str +=
|
|
75
|
-
str +=
|
|
76
|
-
str +=
|
|
77
|
-
str +=
|
|
74
|
+
str += `username="${ cred.username }"`;
|
|
75
|
+
if (realm) { str += `,realm="${ realm }"`; };
|
|
76
|
+
str += `,nonce="${ this._nonce }"`;
|
|
77
|
+
str += `,cnonce="${ cnonce }"`;
|
|
78
|
+
str += `,nc=${ nc}`;
|
|
79
|
+
str += `,qop=${ qop}`;
|
|
80
|
+
str += `,digest-uri="${ uri }"`;
|
|
78
81
|
var base = crypto.createHash('md5')
|
|
79
82
|
.update(cred.username)
|
|
80
83
|
.update(':')
|
|
@@ -112,17 +115,17 @@
|
|
|
112
115
|
.update(':')
|
|
113
116
|
.update(ha2)
|
|
114
117
|
.digest('hex');
|
|
115
|
-
str +=
|
|
118
|
+
str += `,response=${ digest}`;
|
|
116
119
|
if (this._charset == 'utf-8') { str += ',charset=utf-8'; }
|
|
117
|
-
if (cred.authzid) { str +=
|
|
120
|
+
if (cred.authzid) { str += `authzid="${ cred.authzid }"`; }
|
|
118
121
|
return str;
|
|
119
122
|
};
|
|
120
123
|
|
|
121
124
|
/**
|
|
122
125
|
* Decode a challenge issued by the server.
|
|
123
126
|
*
|
|
124
|
-
* @param {String} chal
|
|
125
|
-
* @
|
|
127
|
+
* @param {String} chal Challenge string from server.
|
|
128
|
+
* @returns {Mechanism} for chaining
|
|
126
129
|
* @api public
|
|
127
130
|
*/
|
|
128
131
|
Mechanism.prototype.challenge = function (chal) {
|
|
@@ -146,6 +149,8 @@
|
|
|
146
149
|
/**
|
|
147
150
|
* Parse challenge.
|
|
148
151
|
*
|
|
152
|
+
* @param {String} chal Challenge string to parse.
|
|
153
|
+
* @returns {Object} Parsed directives.
|
|
149
154
|
* @api private
|
|
150
155
|
*/
|
|
151
156
|
function parse(chal) {
|
|
@@ -166,8 +171,8 @@
|
|
|
166
171
|
* genNonce(10)();
|
|
167
172
|
* // => "FDaS435D2z"
|
|
168
173
|
*
|
|
169
|
-
* @param {Number} len
|
|
170
|
-
* @
|
|
174
|
+
* @param {Number} len Length of the nonce.
|
|
175
|
+
* @returns {Function} A function that generates a random nonce.
|
|
171
176
|
* @api private
|
|
172
177
|
*/
|
|
173
178
|
function genNonce(len) {
|
|
@@ -180,7 +185,7 @@
|
|
|
180
185
|
buf.push(chars[Math.random() * charlen | 0]);
|
|
181
186
|
}
|
|
182
187
|
return buf.join('');
|
|
183
|
-
}
|
|
188
|
+
};
|
|
184
189
|
}
|
|
185
190
|
|
|
186
191
|
exports = module.exports = Mechanism;
|
package/lib/sasl/external.js
CHANGED
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
* This class implements the EXTERNAL SASL mechanism.
|
|
15
15
|
*
|
|
16
16
|
* The EXTERNAL SASL mechanism provides support for authentication using
|
|
17
|
-
* credentials established by external means.
|
|
17
|
+
* credentials established by external means.
|
|
18
18
|
*
|
|
19
19
|
* References:
|
|
20
20
|
* - [RFC 4422](http://tools.ietf.org/html/rfc4422)
|
|
21
21
|
*
|
|
22
|
+
* @returns {void}
|
|
22
23
|
* @api public
|
|
23
24
|
*/
|
|
24
25
|
function Mechanism() {
|
|
@@ -33,7 +34,8 @@
|
|
|
33
34
|
* Options:
|
|
34
35
|
* - `authzid` authorization identity (optional)
|
|
35
36
|
*
|
|
36
|
-
* @param {Object} cred
|
|
37
|
+
* @param {Object} cred Credentials containing optional authzid.
|
|
38
|
+
* @returns {String} The authorization identity or empty string.
|
|
37
39
|
* @api public
|
|
38
40
|
*/
|
|
39
41
|
Mechanism.prototype.response = function (cred) {
|
|
@@ -43,10 +45,11 @@
|
|
|
43
45
|
/**
|
|
44
46
|
* Decode a challenge issued by the server.
|
|
45
47
|
*
|
|
46
|
-
* @param {String} chal
|
|
48
|
+
* @param {String} chal Challenge string from server.
|
|
49
|
+
* @returns {void}
|
|
47
50
|
* @api public
|
|
48
51
|
*/
|
|
49
|
-
Mechanism.prototype.challenge = function (chal) {
|
|
52
|
+
Mechanism.prototype.challenge = function (chal) { // eslint-disable-line no-unused-vars
|
|
50
53
|
};
|
|
51
54
|
|
|
52
55
|
exports = module.exports = Mechanism;
|
package/lib/sasl/factory.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
/**
|
|
12
12
|
* `Factory` constructor.
|
|
13
13
|
*
|
|
14
|
+
* @returns {void}
|
|
14
15
|
* @api public
|
|
15
16
|
*/
|
|
16
17
|
function Factory() {
|
|
@@ -27,9 +28,9 @@
|
|
|
27
28
|
*
|
|
28
29
|
* factory.use('XFOO', FooMechanism);
|
|
29
30
|
*
|
|
30
|
-
* @param {String|Mechanism} name
|
|
31
|
-
* @param {Mechanism} mech
|
|
32
|
-
* @
|
|
31
|
+
* @param {String|Mechanism} name Mechanism name or constructor.
|
|
32
|
+
* @param {Mechanism} mech Mechanism constructor when name is a string.
|
|
33
|
+
* @returns {Factory} for chaining
|
|
33
34
|
* @api public
|
|
34
35
|
*/
|
|
35
36
|
Factory.prototype.use = function(name, mech) {
|
|
@@ -50,8 +51,8 @@
|
|
|
50
51
|
*
|
|
51
52
|
* var mech = factory.create(['FOO', 'BAR']);
|
|
52
53
|
*
|
|
53
|
-
* @param {Array} mechs
|
|
54
|
-
* @
|
|
54
|
+
* @param {Array} mechs List of supported mechanism names.
|
|
55
|
+
* @returns {Mechanism} A new mechanism instance or null.
|
|
55
56
|
* @api public
|
|
56
57
|
*/
|
|
57
58
|
Factory.prototype.create = function(mechs) {
|
package/lib/sasl/oauthbearer.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* References:
|
|
19
19
|
* - [RFC 4616](http://tools.ietf.org/html/rfc4616)
|
|
20
20
|
*
|
|
21
|
+
* @returns {void}
|
|
21
22
|
* @api public
|
|
22
23
|
*/
|
|
23
24
|
function Mechanism() {
|
|
@@ -33,13 +34,14 @@
|
|
|
33
34
|
* - `token` an OAuth token
|
|
34
35
|
* - `authzid` authorization identity (optional)
|
|
35
36
|
*
|
|
36
|
-
* @param {Object} cred
|
|
37
|
+
* @param {Object} cred Credentials containing token and optional authzid.
|
|
38
|
+
* @returns {String} The encoded OAUTHBEARER response string.
|
|
37
39
|
* @api public
|
|
38
40
|
*/
|
|
39
41
|
Mechanism.prototype.response = function (cred) {
|
|
40
42
|
var str = 'n,';
|
|
41
43
|
if (cred.authzid) {
|
|
42
|
-
str +=
|
|
44
|
+
str += `a=${ cred.authzid}`;
|
|
43
45
|
}
|
|
44
46
|
str += ',%x01,auth=Bearer ';
|
|
45
47
|
str += cred.token;
|
|
@@ -50,11 +52,11 @@
|
|
|
50
52
|
/**
|
|
51
53
|
* Decode a challenge issued by the server.
|
|
52
54
|
*
|
|
53
|
-
* @param {String} chal
|
|
54
|
-
* @
|
|
55
|
+
* @param {String} chal Challenge string from server.
|
|
56
|
+
* @returns {Mechanism} for chaining
|
|
55
57
|
* @api public
|
|
56
58
|
*/
|
|
57
|
-
Mechanism.prototype.challenge = function (chal) {
|
|
59
|
+
Mechanism.prototype.challenge = function (chal) { // eslint-disable-line no-unused-vars
|
|
58
60
|
return this;
|
|
59
61
|
};
|
|
60
62
|
|
package/lib/sasl/plain.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* References:
|
|
21
21
|
* - [RFC 4616](http://tools.ietf.org/html/rfc4616)
|
|
22
22
|
*
|
|
23
|
+
* @returns {void}
|
|
23
24
|
* @api public
|
|
24
25
|
*/
|
|
25
26
|
function Mechanism() {
|
|
@@ -36,7 +37,8 @@
|
|
|
36
37
|
* - `password`
|
|
37
38
|
* - `authzid` authorization identity (optional)
|
|
38
39
|
*
|
|
39
|
-
* @param {Object} cred
|
|
40
|
+
* @param {Object} cred Credentials containing username, password, and optional authzid.
|
|
41
|
+
* @returns {String} The encoded PLAIN response string.
|
|
40
42
|
* @api public
|
|
41
43
|
*/
|
|
42
44
|
Mechanism.prototype.response = function (cred) {
|
|
@@ -52,11 +54,11 @@
|
|
|
52
54
|
/**
|
|
53
55
|
* Decode a challenge issued by the server.
|
|
54
56
|
*
|
|
55
|
-
* @param {String} chal
|
|
56
|
-
* @
|
|
57
|
+
* @param {String} chal Challenge string from server.
|
|
58
|
+
* @returns {Mechanism} for chaining
|
|
57
59
|
* @api public
|
|
58
60
|
*/
|
|
59
|
-
Mechanism.prototype.challenge = function (chal) {
|
|
61
|
+
Mechanism.prototype.challenge = function (chal) { // eslint-disable-line no-unused-vars
|
|
60
62
|
return this;
|
|
61
63
|
};
|
|
62
64
|
|
package/lib/sasl/scram.js
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
var CLIENT_KEY = 'Client Key';
|
|
12
12
|
var SERVER_KEY = 'Server Key';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Constructs a SCRAM SASL mechanism instance.
|
|
16
|
+
* @param {Object} [options] Optional configuration with a genNonce function.
|
|
17
|
+
* @constructs Mechanism
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
14
20
|
function Mechanism(options) {
|
|
15
21
|
options = options || {};
|
|
16
22
|
this._genNonce = options.genNonce || utils.genNonce;
|
|
@@ -40,6 +46,8 @@
|
|
|
40
46
|
/**
|
|
41
47
|
* Parse challenge.
|
|
42
48
|
*
|
|
49
|
+
* @param {String} chal Challenge string to parse.
|
|
50
|
+
* @returns {Object} Parsed key-value pairs.
|
|
43
51
|
* @api private
|
|
44
52
|
*/
|
|
45
53
|
function parse(chal) {
|
|
@@ -60,15 +68,15 @@
|
|
|
60
68
|
|
|
61
69
|
var authzid = '';
|
|
62
70
|
if (cred.authzid) {
|
|
63
|
-
authzid =
|
|
71
|
+
authzid = `a=${ utils.saslname(cred.authzid)}`;
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
mech._gs2Header =
|
|
74
|
+
mech._gs2Header = `n,${ authzid },`;
|
|
67
75
|
|
|
68
|
-
var nonce =
|
|
69
|
-
var username =
|
|
76
|
+
var nonce = `r=${ mech._cnonce}`;
|
|
77
|
+
var username = `n=${ utils.saslname(cred.username || '')}`;
|
|
70
78
|
|
|
71
|
-
mech._clientFirstMessageBare = username
|
|
79
|
+
mech._clientFirstMessageBare = `${username },${ nonce}`;
|
|
72
80
|
var result = mech._gs2Header + mech._clientFirstMessageBare;
|
|
73
81
|
|
|
74
82
|
mech._stage = 'two';
|
|
@@ -79,11 +87,11 @@
|
|
|
79
87
|
|
|
80
88
|
RESP.two = function (mech, cred) {
|
|
81
89
|
var gs2Header = Buffer.from(mech._gs2Header).toString('base64');
|
|
82
|
-
mech._clientFinalMessageWithoutProof =
|
|
90
|
+
mech._clientFinalMessageWithoutProof = `c=${ gs2Header },r=${ mech._nonce}`;
|
|
83
91
|
|
|
84
92
|
var saltedPassword, clientKey, serverKey;
|
|
85
93
|
|
|
86
|
-
var algorithm = cred.mechanism.substring(6).toLowerCase().replace(
|
|
94
|
+
var algorithm = cred.mechanism.substring(6).toLowerCase().replace('-', '');
|
|
87
95
|
|
|
88
96
|
// If our cached salt is the same, we can reuse cached credentials to speed
|
|
89
97
|
// up the hashing process.
|
|
@@ -103,16 +111,16 @@
|
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
var storedKey = bitops.H(algorithm, clientKey);
|
|
106
|
-
var authMessage = mech._clientFirstMessageBare
|
|
107
|
-
mech._challenge
|
|
108
|
-
mech._clientFinalMessageWithoutProof
|
|
114
|
+
var authMessage = `${mech._clientFirstMessageBare },${
|
|
115
|
+
mech._challenge },${
|
|
116
|
+
mech._clientFinalMessageWithoutProof}`;
|
|
109
117
|
var clientSignature = bitops.HMAC(algorithm, storedKey, authMessage);
|
|
110
118
|
|
|
111
119
|
var clientProof = bitops.XOR(clientKey, clientSignature).toString('base64');
|
|
112
120
|
|
|
113
121
|
mech._serverSignature = bitops.HMAC(algorithm, serverKey, authMessage);
|
|
114
122
|
|
|
115
|
-
var result = mech._clientFinalMessageWithoutProof
|
|
123
|
+
var result = `${mech._clientFinalMessageWithoutProof },p=${ clientProof}`;
|
|
116
124
|
|
|
117
125
|
mech._stage = 'final';
|
|
118
126
|
|
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));
|