@tdxvolt/volt-client-grpc 0.16.0-beta.9 → 0.18.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/README.md +39 -3
- package/lib/index.cjs +458 -515
- package/package.json +2 -4
- package/protobuf/tdx/volt_api/data/v1/sqlite_database_api.proto +49 -13
- package/protobuf/tdx/volt_api/data/v1/sqlite_server_api.proto +3 -0
- package/protobuf/tdx/volt_api/volt/v1/discovery_api.proto +0 -4
- package/protobuf/tdx/volt_api/volt/v1/ssi.proto +31 -5
- package/protobuf/tdx/volt_api/volt/v1/ssi_api.proto +121 -33
- package/protobuf/tdx/volt_api/volt/v1/terminal_api.proto +42 -0
- package/protobuf/tdx/volt_api/volt/v1/volt.proto +152 -140
- package/protobuf/tdx/volt_api/volt/v1/volt_api.proto +168 -270
- package/protobuf/tdx/volt_api/volt/v1/wire_api.proto +4 -0
- package/src/constants.js +4 -58
- package/src/grpc-call.js +144 -51
- package/src/grpc-utils.js +8 -4
- package/src/rpc-invocation.js +96 -46
- package/src/utils.js +5 -119
- package/src/volt-client-internal.js +109 -78
- package/src/volt-client.js +33 -12
- package/src/volt-connection.js +25 -16
- package/src/volt-credential.js +39 -124
package/src/utils.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import ip from "ip";
|
|
2
2
|
import _ from "lodash";
|
|
3
|
-
|
|
4
|
-
import bs58 from "bs58";
|
|
3
|
+
export { voltUtils as crypto } from "@tdxvolt/volt-client-web/js";
|
|
5
4
|
|
|
6
5
|
import { constants } from "./constants.js";
|
|
7
6
|
|
|
8
|
-
const { pki } = forge;
|
|
9
|
-
|
|
10
7
|
export const createSecureContextOptions = (cryptoOptions) => {
|
|
11
8
|
const tlsOptions = {
|
|
12
9
|
cert: Buffer.from(cryptoOptions.cert),
|
|
@@ -36,53 +33,7 @@ export const getServiceAddress = (mdnsService) => {
|
|
|
36
33
|
return address;
|
|
37
34
|
};
|
|
38
35
|
|
|
39
|
-
export const
|
|
40
|
-
return pki.privateKeyToPem(keyPair.privateKey);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export const publicKeyToPem = (keyPair) => {
|
|
44
|
-
return pki.publicKeyToPem(keyPair.publicKey);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const deserialisePEM = (pem) => {
|
|
48
|
-
return pem.replace(/\\r\\n/g, "\r\n");
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export const removeCarriageReturn = (inp) => {
|
|
52
|
-
return inp.replace(/\r\n/g, "\n");
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export const createBase58Hash = (inp) => {
|
|
56
|
-
const md = forge.md.sha256.create();
|
|
57
|
-
md.update(inp);
|
|
58
|
-
const hashBuf = Buffer.from(md.digest().getBytes(), "binary");
|
|
59
|
-
return bs58.encode(hashBuf);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export const createPublicKeyFingerprint = (key) => {
|
|
63
|
-
// Remove carriage returns before creating hash to bring into line with openSSL / volt format.
|
|
64
|
-
return createBase58Hash(removeCarriageReturn(key));
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const base64ToBase64Url = (base64) => {
|
|
68
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const signBase64 = (key, msg) => {
|
|
72
|
-
const md = forge.md.sha256.create();
|
|
73
|
-
md.update(msg);
|
|
74
|
-
const signature = key.sign(md);
|
|
75
|
-
const base64Standard = forge.util.encode64(signature);
|
|
76
|
-
return base64ToBase64Url(base64Standard);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export const verifyBase64 = (key, msg, signature) => {
|
|
80
|
-
const md = forge.md.sha256.create();
|
|
81
|
-
md.update(msg);
|
|
82
|
-
return key.verify(md.digest().bytes(), forge.util.decode64(signature));
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
export const getDIDResolutionURL = (didIn) => {
|
|
36
|
+
export const getDIDResolutionURL = (didIn, registryUrl = "") => {
|
|
86
37
|
const did = didIn.toLowerCase().split(":");
|
|
87
38
|
if (did.length < 3) {
|
|
88
39
|
return "";
|
|
@@ -92,75 +43,10 @@ export const getDIDResolutionURL = (didIn) => {
|
|
|
92
43
|
return "";
|
|
93
44
|
}
|
|
94
45
|
|
|
95
|
-
if (did[1] !== "
|
|
46
|
+
if (did[1] !== "volt") {
|
|
96
47
|
return "";
|
|
97
48
|
}
|
|
98
49
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (did.length === 3) {
|
|
102
|
-
hostName = constants.defaultDIDHostName;
|
|
103
|
-
didGUID = did[2];
|
|
104
|
-
} else {
|
|
105
|
-
hostName = did[2];
|
|
106
|
-
didGUID = did[3];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return `https://${hostName}/api/identity/${didGUID}`;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const forgePrivateKeyToPem = (decryptedKey) => {
|
|
113
|
-
// Convert a Forge private key to an ASN.1 RSAPrivateKey
|
|
114
|
-
const asnPrivateKey = pki.privateKeyToAsn1(decryptedKey);
|
|
115
|
-
|
|
116
|
-
// Wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo
|
|
117
|
-
const privateKeyInfo = pki.wrapRsaPrivateKey(asnPrivateKey);
|
|
118
|
-
|
|
119
|
-
// Convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM
|
|
120
|
-
return pki.privateKeyInfoToPem(privateKeyInfo);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
export const createRSAKey = (passphrase = "") => {
|
|
124
|
-
return new Promise((resolve, reject) => {
|
|
125
|
-
pki.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (err, keypair) => {
|
|
126
|
-
if (err) {
|
|
127
|
-
reject(err);
|
|
128
|
-
} else {
|
|
129
|
-
let pem;
|
|
130
|
-
if (passphrase) {
|
|
131
|
-
pem = pki.encryptRsaPrivateKey(keypair.privateKey, passphrase);
|
|
132
|
-
} else {
|
|
133
|
-
pem = forgePrivateKeyToPem(keypair.privateKey);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
resolve({ keypair, pem });
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
export const sha256Base64 = (msg) => {
|
|
143
|
-
const md = forge.md.sha256.create();
|
|
144
|
-
md.update(msg);
|
|
145
|
-
const base64Standard = forge.util.encode64(md.digest().bytes());
|
|
146
|
-
return base64ToBase64Url(base64Standard);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
export const decryptPrivateKey = (pem, passphrase) => {
|
|
150
|
-
const decryptedKey = pki.decryptRsaPrivateKey(pem, passphrase);
|
|
151
|
-
return forgePrivateKeyToPem(decryptedKey);
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
export const jwsSignature = (signature) => {
|
|
155
|
-
//
|
|
156
|
-
// Use an unattached payload JWS signature for the DID document proof.
|
|
157
|
-
// This is a JWS with an empty payload and a detached payload digest.
|
|
158
|
-
//
|
|
159
|
-
// (https://tools.ietf.org/html/rfc7797#appendix-F) as used in the VC spec
|
|
160
|
-
// (https://www.w3.org/TR/vc-data-model/).
|
|
161
|
-
//
|
|
162
|
-
const jwsHeader = base64ToBase64Url(
|
|
163
|
-
forge.util.encode64('{"alg":"RS256","b64": false}')
|
|
164
|
-
);
|
|
165
|
-
return jwsHeader + ".." + signature;
|
|
50
|
+
const lookupUrl = registryUrl || `https://${constants.defaultDIDHostName}`;
|
|
51
|
+
return `${lookupUrl}/did/${didIn}`;
|
|
166
52
|
};
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import debug from "debug";
|
|
3
3
|
import ip from "ip";
|
|
4
4
|
import bent from "bent";
|
|
5
|
-
import forge from "node-forge";
|
|
6
5
|
import lodash from "lodash";
|
|
7
6
|
import VoltConnection from "./volt-connection.js";
|
|
8
7
|
import { join } from "path";
|
|
@@ -17,8 +16,10 @@ import { constants } from "./constants.js";
|
|
|
17
16
|
import GRPCCall from "./grpc-call.js";
|
|
18
17
|
import RpcInvocation from "./rpc-invocation.js";
|
|
19
18
|
import { v4 as generateId } from "uuid";
|
|
19
|
+
import { voltUtils as voltClientUtils } from "@tdxvolt/volt-client-web/js";
|
|
20
|
+
|
|
21
|
+
const { publicKeyToPem, removeCarriageReturn, signBase64 } = voltClientUtils;
|
|
20
22
|
|
|
21
|
-
const { pki } = forge;
|
|
22
23
|
const { filter, pick } = lodash;
|
|
23
24
|
|
|
24
25
|
const log = debug("volt-client-grpc:volt-client-internal");
|
|
@@ -54,9 +55,15 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
54
55
|
// This is the signing CA used by the volt.
|
|
55
56
|
this._credential.cache.ca = authenticateResponse.chain;
|
|
56
57
|
|
|
57
|
-
//
|
|
58
|
+
// The session id is assigned by the volt.
|
|
58
59
|
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
59
60
|
|
|
61
|
+
if (!this._credential.cache.identity_did) {
|
|
62
|
+
// Identity DID has been assigned by the volt.
|
|
63
|
+
this._credential.cache.identity_did =
|
|
64
|
+
authenticateResponse.identity_did;
|
|
65
|
+
}
|
|
66
|
+
|
|
60
67
|
this._credential.saveCache();
|
|
61
68
|
break;
|
|
62
69
|
}
|
|
@@ -67,6 +74,20 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
67
74
|
case "POLICY_DECISION_PROMPT":
|
|
68
75
|
case "POLICY_DECISION_PENDING": {
|
|
69
76
|
log("access pending approval - waiting...");
|
|
77
|
+
|
|
78
|
+
// This is the signing CA used by the volt.
|
|
79
|
+
this._credential.cache.ca = authenticateResponse.chain;
|
|
80
|
+
|
|
81
|
+
// The session id is assigned by the volt.
|
|
82
|
+
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
83
|
+
|
|
84
|
+
if (!this._credential.cache.identity_did) {
|
|
85
|
+
// Identity DID has been assigned by the volt.
|
|
86
|
+
this._credential.cache.identity_did =
|
|
87
|
+
authenticateResponse.identity_did;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this._credential.saveCache();
|
|
70
91
|
break;
|
|
71
92
|
}
|
|
72
93
|
default:
|
|
@@ -91,7 +112,10 @@ function findDIDDocumentService(document, serviceType) {
|
|
|
91
112
|
return matches;
|
|
92
113
|
}
|
|
93
114
|
|
|
94
|
-
export async function authenticateInternal(
|
|
115
|
+
export async function authenticateInternal(
|
|
116
|
+
sessionOnly = false,
|
|
117
|
+
ownDID = false
|
|
118
|
+
) {
|
|
95
119
|
try {
|
|
96
120
|
// Check if we need to resolve a Relay volt.
|
|
97
121
|
if (
|
|
@@ -103,7 +127,7 @@ export async function authenticateInternal(useDID = true) {
|
|
|
103
127
|
log("attempting to retrieve Relay information from %s", relayURL);
|
|
104
128
|
this._voltConfig.relay = await fetchVoltConfig.call(
|
|
105
129
|
this,
|
|
106
|
-
`${relayURL}/
|
|
130
|
+
`${relayURL}/discover`
|
|
107
131
|
);
|
|
108
132
|
|
|
109
133
|
if (this._voltConfig.relay.ca_pem) {
|
|
@@ -128,8 +152,8 @@ export async function authenticateInternal(useDID = true) {
|
|
|
128
152
|
}
|
|
129
153
|
|
|
130
154
|
const keyPair = this._credential.getKey();
|
|
131
|
-
const publicKeyPem =
|
|
132
|
-
|
|
155
|
+
const publicKeyPem = removeCarriageReturn(
|
|
156
|
+
publicKeyToPem(keyPair.publicKey)
|
|
133
157
|
);
|
|
134
158
|
|
|
135
159
|
if (!this._credential.cache.bindIp) {
|
|
@@ -142,12 +166,12 @@ export async function authenticateInternal(useDID = true) {
|
|
|
142
166
|
|
|
143
167
|
// Form the request message.
|
|
144
168
|
const authenticateRequest = {
|
|
145
|
-
|
|
169
|
+
client_name: this._config.client_name,
|
|
146
170
|
host: this._credential.cache.bindIp
|
|
147
171
|
};
|
|
148
172
|
|
|
149
173
|
if (this._voltConfig.challenge_code) {
|
|
150
|
-
authenticateRequest.challenge =
|
|
174
|
+
authenticateRequest.challenge = signBase64(
|
|
151
175
|
keyPair.privateKey,
|
|
152
176
|
this._voltConfig.challenge_code
|
|
153
177
|
);
|
|
@@ -157,68 +181,58 @@ export async function authenticateInternal(useDID = true) {
|
|
|
157
181
|
);
|
|
158
182
|
}
|
|
159
183
|
|
|
160
|
-
if (
|
|
184
|
+
if (sessionOnly) {
|
|
185
|
+
// We want to create a session-based authentication based simply on our key, rather than a
|
|
186
|
+
// fully-blown DID-based authentication.
|
|
187
|
+
log("creating session-only request");
|
|
161
188
|
authenticateRequest.public_key = publicKeyPem;
|
|
162
|
-
} else if (!this._credential.cache.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
created: new Date().toISOString(),
|
|
202
|
-
jws: proofSignature,
|
|
203
|
-
proofPurpose: "assertionMethod",
|
|
204
|
-
type: "RsaSignature2018",
|
|
205
|
-
verificationMethod: `${did}#key-1`
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
// Set the DID document with proof in the authenticate request.
|
|
209
|
-
authenticateRequest.did_document = JSON.stringify(didDocument, null, 2);
|
|
210
|
-
|
|
211
|
-
// Now sign the entire DID document, including the proof and set it in the
|
|
212
|
-
// bind request. This is required by the Volt to verify that we have the private key for the DID.
|
|
213
|
-
authenticateRequest.did_document_signature = voltUtils.signBase64(
|
|
214
|
-
keyPair.privateKey,
|
|
215
|
-
authenticateRequest.did_document
|
|
216
|
-
);
|
|
189
|
+
} else if (!this._credential.cache.identity_did) {
|
|
190
|
+
if (!ownDID) {
|
|
191
|
+
// We don't want to have to manage our own DID, so we ask the Volt to do it for us.
|
|
192
|
+
// The Volt will create a new DID and return it to us in the response.
|
|
193
|
+
log("creating DID-based request");
|
|
194
|
+
authenticateRequest.did_public_key = publicKeyPem;
|
|
195
|
+
} else {
|
|
196
|
+
// We want to create and manage our own DID.
|
|
197
|
+
const did =
|
|
198
|
+
(this._credential.cache.identity_did = `did:volt:${generateId()}`);
|
|
199
|
+
|
|
200
|
+
// Order of properties in the DID document is important,
|
|
201
|
+
// they must be in alphabetical order throughout the document.
|
|
202
|
+
// This is because the DID document is signed and the signature
|
|
203
|
+
// is over the entire document.
|
|
204
|
+
const didDocument = {
|
|
205
|
+
"@context": "https://www.w3.org/ns/did/v1",
|
|
206
|
+
authentication: [`${did}#key-1`],
|
|
207
|
+
controller: did,
|
|
208
|
+
description: this._config.client_name,
|
|
209
|
+
id: did,
|
|
210
|
+
verificationMethod: [
|
|
211
|
+
{
|
|
212
|
+
id: `${did}#key-1`,
|
|
213
|
+
publicKeyPem: publicKeyPem,
|
|
214
|
+
type: "Ed25519VerificationKey2018"
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Set the DID document with proof in the authenticate request.
|
|
220
|
+
authenticateRequest.did_document = JSON.stringify(didDocument, null, 2);
|
|
221
|
+
|
|
222
|
+
// Now sign the entire DID document, including the proof and set it in the
|
|
223
|
+
// bind request. This is required by the Volt to verify that we have the private key for the DID.
|
|
224
|
+
authenticateRequest.did_document_signature = signBase64(
|
|
225
|
+
keyPair.privateKey,
|
|
226
|
+
authenticateRequest.did_document
|
|
227
|
+
);
|
|
217
228
|
|
|
218
|
-
|
|
219
|
-
|
|
229
|
+
log("did document is %s", authenticateRequest.did_document);
|
|
230
|
+
log("did signature is %s", authenticateRequest.did_document_signature);
|
|
231
|
+
}
|
|
220
232
|
} else {
|
|
221
|
-
|
|
233
|
+
// We already have a DID cached, so re-use it.
|
|
234
|
+
log("using cached DID %s", this._credential.cache.identity_did);
|
|
235
|
+
authenticateRequest.did = this._credential.cache.identity_did;
|
|
222
236
|
}
|
|
223
237
|
|
|
224
238
|
log("authenticate TTL is ", this._config.bindRequestTTL);
|
|
@@ -287,6 +301,11 @@ export function connectInternal(helloPayload) {
|
|
|
287
301
|
this._voltConnection.on("error", (err) => {
|
|
288
302
|
// Should auto-reconnect.
|
|
289
303
|
log("Volt connection error [%s]", err.message);
|
|
304
|
+
if (this._connected) {
|
|
305
|
+
onDisconnected();
|
|
306
|
+
this.emit("connected", false);
|
|
307
|
+
}
|
|
308
|
+
|
|
290
309
|
if (err.message.endsWith("session invalid")) {
|
|
291
310
|
log("Volt session invalid - clearing session");
|
|
292
311
|
delete this._credential.cache.session_id;
|
|
@@ -294,11 +313,6 @@ export function connectInternal(helloPayload) {
|
|
|
294
313
|
this._credential.saveCache();
|
|
295
314
|
}
|
|
296
315
|
|
|
297
|
-
if (this._connected) {
|
|
298
|
-
onDisconnected();
|
|
299
|
-
this.emit("connected", false);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
316
|
this.emit("connectionError", err);
|
|
303
317
|
});
|
|
304
318
|
|
|
@@ -312,10 +326,24 @@ export function connectInternal(helloPayload) {
|
|
|
312
326
|
|
|
313
327
|
this._voltConnection.on("invoke_request", (invoke_request) => {
|
|
314
328
|
const invokeId = invoke_request.invoke_id;
|
|
315
|
-
|
|
316
|
-
|
|
329
|
+
|
|
330
|
+
let rpcInvocation = this._activeRPC[invokeId];
|
|
331
|
+
|
|
332
|
+
if (rpcInvocation) {
|
|
333
|
+
if (rpcInvocation.started) {
|
|
334
|
+
rpcInvocation.parsePayload(invoke_request);
|
|
335
|
+
} else {
|
|
336
|
+
rpcInvocation
|
|
337
|
+
.initialise(invoke_request)
|
|
338
|
+
.then(() => {
|
|
339
|
+
this.emit("invoke_request", rpcInvocation);
|
|
340
|
+
})
|
|
341
|
+
.catch((err) => {
|
|
342
|
+
log("invoke_request - error [%s]", err.message);
|
|
343
|
+
});
|
|
344
|
+
}
|
|
317
345
|
} else {
|
|
318
|
-
|
|
346
|
+
rpcInvocation = new RpcInvocation(this, this._voltConnection);
|
|
319
347
|
|
|
320
348
|
rpcInvocation.on("end", () => {
|
|
321
349
|
log("removing active RPC [%s]", invokeId);
|
|
@@ -324,9 +352,11 @@ export function connectInternal(helloPayload) {
|
|
|
324
352
|
|
|
325
353
|
rpcInvocation
|
|
326
354
|
.initialise(invoke_request)
|
|
327
|
-
.then(() => {
|
|
355
|
+
.then((response) => {
|
|
328
356
|
this._activeRPC[invokeId] = rpcInvocation;
|
|
329
|
-
|
|
357
|
+
if (!response.key_exchange) {
|
|
358
|
+
this.emit("invoke_request", rpcInvocation);
|
|
359
|
+
}
|
|
330
360
|
})
|
|
331
361
|
.catch((err) => {
|
|
332
362
|
log("invoke_request - error [%s]", err.message);
|
|
@@ -472,6 +502,7 @@ export async function fetchVoltConfig(discovery_url) {
|
|
|
472
502
|
"public_key",
|
|
473
503
|
"fingerprint",
|
|
474
504
|
"address",
|
|
505
|
+
"http_address",
|
|
475
506
|
"ca_pem",
|
|
476
507
|
"challenge_code",
|
|
477
508
|
"cloud",
|
package/src/volt-client.js
CHANGED
|
@@ -59,6 +59,7 @@ export class VoltClient extends EventEmitter {
|
|
|
59
59
|
/**
|
|
60
60
|
* Initialises a binding and connection to the volt.
|
|
61
61
|
* @param {string} [config] - the location of the Volt configuration file, or a configuration object
|
|
62
|
+
* @param {object} [extras] - additional configuration properties
|
|
62
63
|
*/
|
|
63
64
|
async initialise(config, extras = {}) {
|
|
64
65
|
try {
|
|
@@ -74,10 +75,15 @@ export class VoltClient extends EventEmitter {
|
|
|
74
75
|
configPath = path.resolve(config);
|
|
75
76
|
log("Attempt to load config from file %s", configPath);
|
|
76
77
|
try {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
if (fs.existsSync(configPath)) {
|
|
79
|
+
const configContents = await readFile(
|
|
80
|
+
new URL(configPath, import.meta.url)
|
|
81
|
+
);
|
|
82
|
+
configJSON = JSON.parse(configContents);
|
|
83
|
+
} else {
|
|
84
|
+
// There is currently no file at the given configuration path.
|
|
85
|
+
configJSON = {};
|
|
86
|
+
}
|
|
81
87
|
} catch (err) {
|
|
82
88
|
log("failure loading config file %s [%s]", configPath, err.message);
|
|
83
89
|
throw new Error(
|
|
@@ -94,15 +100,15 @@ export class VoltClient extends EventEmitter {
|
|
|
94
100
|
throw new Error("config argument is required to be an object");
|
|
95
101
|
}
|
|
96
102
|
|
|
103
|
+
this._config = { ...extras, ...configJSON };
|
|
104
|
+
|
|
97
105
|
if (
|
|
98
|
-
!
|
|
99
|
-
typeof
|
|
106
|
+
!this._config.client_name ||
|
|
107
|
+
typeof this._config.client_name !== "string"
|
|
100
108
|
) {
|
|
101
109
|
throw new Error("client_name property required in config");
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
this._config = { ...configJSON, ...extras };
|
|
105
|
-
|
|
106
112
|
let voltDID = "";
|
|
107
113
|
let voltHttpAddress = "";
|
|
108
114
|
if (typeof this._config.volt === "string") {
|
|
@@ -111,6 +117,7 @@ export class VoltClient extends EventEmitter {
|
|
|
111
117
|
} else {
|
|
112
118
|
voltHttpAddress = this._config.volt;
|
|
113
119
|
}
|
|
120
|
+
delete this._config.volt;
|
|
114
121
|
} else if (typeof this._config.volt !== "object") {
|
|
115
122
|
throw new Error("configuration is missing the 'volt' object");
|
|
116
123
|
} else {
|
|
@@ -125,7 +132,7 @@ export class VoltClient extends EventEmitter {
|
|
|
125
132
|
} else if (voltHttpAddress && !this._config?.volt?.id) {
|
|
126
133
|
const discoConfig = await fetchVoltConfig.call(
|
|
127
134
|
this,
|
|
128
|
-
`${voltHttpAddress}/
|
|
135
|
+
`${voltHttpAddress}/discover`
|
|
129
136
|
);
|
|
130
137
|
this._config = {
|
|
131
138
|
...this._config,
|
|
@@ -134,8 +141,12 @@ export class VoltClient extends EventEmitter {
|
|
|
134
141
|
}
|
|
135
142
|
|
|
136
143
|
this._voltConfig = this._config.volt;
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
if (voltDID) {
|
|
145
|
+
this._voltConfig.did = voltDID;
|
|
146
|
+
}
|
|
147
|
+
if (voltHttpAddress) {
|
|
148
|
+
this._voltConfig.http_address = voltHttpAddress;
|
|
149
|
+
}
|
|
139
150
|
|
|
140
151
|
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
141
152
|
throw new Error("'id' property is missing in Volt configuration");
|
|
@@ -183,9 +194,12 @@ export class VoltClient extends EventEmitter {
|
|
|
183
194
|
if (!isBound) {
|
|
184
195
|
// This is usually because of request being denied.
|
|
185
196
|
throw new Error("Volt authentication failed");
|
|
197
|
+
} else {
|
|
198
|
+
// Perist the configuration.
|
|
199
|
+
this._config = this._credential.saveCache();
|
|
186
200
|
}
|
|
187
201
|
|
|
188
|
-
return Promise.resolve();
|
|
202
|
+
return Promise.resolve(this._config);
|
|
189
203
|
} catch (err) {
|
|
190
204
|
log("Failure starting Volt client [%s]", err.message);
|
|
191
205
|
throw err;
|
|
@@ -654,6 +668,13 @@ export class VoltClient extends EventEmitter {
|
|
|
654
668
|
});
|
|
655
669
|
}
|
|
656
670
|
|
|
671
|
+
SqlTransaction(request) {
|
|
672
|
+
const grpcClient = this.getVoltAPIClient();
|
|
673
|
+
|
|
674
|
+
const call = new GRPCCall(this, "Transaction", "METHOD_TYPE_BIDI");
|
|
675
|
+
return call.start(grpcClient, request);
|
|
676
|
+
}
|
|
677
|
+
|
|
657
678
|
/**
|
|
658
679
|
* WireAPI
|
|
659
680
|
*/
|
package/src/volt-connection.js
CHANGED
|
@@ -19,10 +19,9 @@ function _cleanUp(immediateReconnect) {
|
|
|
19
19
|
this._pingTimeoutTimer = 0;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
) {
|
|
22
|
+
if (this._dying) {
|
|
23
|
+
clearTimeout(this._reconnectTimer);
|
|
24
|
+
} else if (immediateReconnect || (this._autoRetry && !this._reconnectTimer)) {
|
|
26
25
|
this._reconnectTimer = setTimeout(
|
|
27
26
|
() => {
|
|
28
27
|
this._reconnectTimer = 0;
|
|
@@ -48,13 +47,18 @@ function _doConnect(connectHello) {
|
|
|
48
47
|
|
|
49
48
|
const grpcClient = this._voltClient.getVoltAPIClient();
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
// Notify the Volt of our ping interval. If the Volt doesn't receive a ping from us
|
|
51
|
+
// within this time, it will assume the connection is dead.
|
|
52
|
+
const defaultHello = {
|
|
53
|
+
ping_interval: this._pingInterval,
|
|
54
|
+
timestamp: Date.now()
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const helloPayload = { ...connectHello } || {};
|
|
58
|
+
|
|
59
|
+
helloPayload.hello = { ...defaultHello, ...connectHello?.hello };
|
|
60
|
+
|
|
61
|
+
log("hello is %j", helloPayload);
|
|
58
62
|
|
|
59
63
|
this.call.on("error", (err) => {
|
|
60
64
|
log("error on connection stream [%s]", err.message);
|
|
@@ -72,17 +76,22 @@ function _doConnect(connectHello) {
|
|
|
72
76
|
clearTimeout(this._pingTimeoutTimer);
|
|
73
77
|
}
|
|
74
78
|
|
|
75
|
-
// Schedule the next ping.
|
|
76
|
-
_startPingTimer.call(this);
|
|
77
|
-
|
|
78
79
|
this.emit("ping", response.ping.timestamp);
|
|
79
80
|
break;
|
|
80
81
|
}
|
|
81
82
|
case "acknowledge": {
|
|
82
83
|
if (!this._connectionId) {
|
|
83
84
|
this._connectionId = response.acknowledge.connection_id;
|
|
85
|
+
|
|
86
|
+
// Set the ping timeout interval to the value returned by the Volt. If we don't
|
|
87
|
+
// hear from the Volt in this time, we'll assume the connection is dead.
|
|
88
|
+
this._timeoutInterval = response.acknowledge.ping_interval;
|
|
89
|
+
|
|
84
90
|
log("connection id is %s", this._connectionId);
|
|
85
91
|
this.emit("connected", this._connectionId);
|
|
92
|
+
|
|
93
|
+
// Schedule the next ping.
|
|
94
|
+
_startPingTimer.call(this);
|
|
86
95
|
}
|
|
87
96
|
break;
|
|
88
97
|
}
|
|
@@ -110,8 +119,6 @@ function _doConnect(connectHello) {
|
|
|
110
119
|
});
|
|
111
120
|
|
|
112
121
|
this.call.start(grpcClient, helloPayload);
|
|
113
|
-
|
|
114
|
-
_startPingTimer.call(this);
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
function _startPingTimer() {
|
|
@@ -138,6 +145,8 @@ function _startPingTimer() {
|
|
|
138
145
|
_connectionTimeoutHandler.bind(this),
|
|
139
146
|
this._timeoutInterval
|
|
140
147
|
);
|
|
148
|
+
|
|
149
|
+
_startPingTimer.call(this);
|
|
141
150
|
}, this._pingInterval);
|
|
142
151
|
}
|
|
143
152
|
|