@tdxvolt/volt-client-grpc 0.11.0 → 0.11.3

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/src/grpc-utils.js CHANGED
@@ -6,77 +6,77 @@ import { createSecureContextOptions } from "./utils.js";
6
6
  const { forEach, isEmpty } = lodash;
7
7
 
8
8
  const serialiseNOP = (arg) => {
9
- return arg;
9
+ return arg;
10
10
  };
11
11
 
12
12
  export function createClient(
13
- grpc,
14
- serviceDescriptors,
15
- address,
16
- credentials,
17
- noSerialise,
18
- voltId,
19
- serviceId,
13
+ grpc,
14
+ serviceDescriptors,
15
+ address,
16
+ credentials,
17
+ noSerialise,
18
+ voltId,
19
+ serviceId,
20
20
  ) {
21
- let grpcCredentials;
22
- if (credentials) {
23
- const ca = credentials.config?.relay?.ca_pem || credentials.cache.ca;
24
- const cert = credentials.cache.cloud_cert || credentials.cache.cert;
25
- const { key } = credentials.cache;
26
-
27
- if (key && cert) {
28
- const cryyptoOptions = { ca, cert, key };
29
- const tlsOptions = createSecureContextOptions(cryyptoOptions);
30
- grpcCredentials = grpc.credentials.createSsl(
31
- tlsOptions.ca,
32
- tlsOptions.key,
33
- tlsOptions.cert,
34
- );
35
- } else {
36
- // Cache has no private key, this implies we are connecting without supplying a client certificate.
37
- // Simply connect using the CA certificate.
38
- grpcCredentials = grpc.credentials.createSsl(Buffer.from(ca));
39
- }
40
- } else {
41
- grpcCredentials = grpc.credentials.createInsecure();
42
- }
43
-
44
- // Force all methods to appear bi-directional, since we manually handle the call flow (see grpc-call.js).
45
- forEach(serviceDescriptors, (method) => {
46
- method.requestStream = true;
47
- method.responseStream = true;
48
- });
49
-
50
- if (noSerialise || voltId) {
51
- if (voltId && !serviceId) {
52
- // We must have a service identifier id when tunnelling in order to be able to route the rpc.
53
- throw new Error("createClient - no serviceId given");
54
- }
55
- forEach(serviceDescriptors, (method) => {
56
- if (voltId) {
57
- // Prepend the resource id to the method path if tunnelling.
58
- if (voltId === serviceId) {
59
- method.path = `/${voltId}${method.path}`;
60
- } else {
61
- method.path = `/${voltId}/${serviceId}${method.path}`;
62
- }
63
- }
64
- if (noSerialise) {
65
- // Save original serialisation methods so we can encrypt/decrypt in the tunnel.
66
- method.requestSerializeOriginal = method.requestSerialize;
67
- method.responseDeserializeOriginal = method.responseDeserialize;
68
-
69
- // Replace default serialisation with NOP, since when tunnelling we encrypt the payload
70
- // before sending it to the channel, so we want it to be treated as a pure byte array (pass-through).
71
- method.requestSerialize = serialiseNOP;
72
- method.responseDeserialize = serialiseNOP;
73
- }
74
- });
75
- }
76
-
77
- const SubClass = grpc.makeGenericClientConstructor(serviceDescriptors);
78
-
79
- return new SubClass(address, grpcCredentials);
21
+ let grpcCredentials;
22
+ if (credentials) {
23
+ const ca = credentials.config?.relay?.ca_pem || credentials.cache.ca;
24
+ const cert = credentials.cache.cloud_cert || credentials.cache.cert;
25
+ const { key } = credentials.cache;
26
+
27
+ if (key && cert) {
28
+ const cryyptoOptions = { ca, cert, key };
29
+ const tlsOptions = createSecureContextOptions(cryyptoOptions);
30
+ grpcCredentials = grpc.credentials.createSsl(
31
+ tlsOptions.ca,
32
+ tlsOptions.key,
33
+ tlsOptions.cert,
34
+ );
35
+ } else {
36
+ // Cache has no private key, this implies we are connecting without supplying a client certificate.
37
+ // Simply connect using the CA certificate.
38
+ grpcCredentials = grpc.credentials.createSsl(Buffer.from(ca));
39
+ }
40
+ } else {
41
+ grpcCredentials = grpc.credentials.createInsecure();
42
+ }
43
+
44
+ // Force all methods to appear bi-directional, since we manually handle the call flow (see grpc-call.js).
45
+ forEach(serviceDescriptors, (method) => {
46
+ method.requestStream = true;
47
+ method.responseStream = true;
48
+ });
49
+
50
+ if (noSerialise || voltId) {
51
+ if (voltId && !serviceId) {
52
+ // We must have a service identifier id when tunnelling in order to be able to route the rpc.
53
+ throw new Error("createClient - no serviceId given");
54
+ }
55
+ forEach(serviceDescriptors, (method) => {
56
+ if (voltId) {
57
+ // Prepend the resource id to the method path if tunnelling.
58
+ if (voltId === serviceId) {
59
+ method.path = `/${voltId}${method.path}`;
60
+ } else {
61
+ method.path = `/${voltId}/${serviceId}${method.path}`;
62
+ }
63
+ }
64
+ if (noSerialise) {
65
+ // Save original serialisation methods so we can encrypt/decrypt in the tunnel.
66
+ method.requestSerializeOriginal = method.requestSerialize;
67
+ method.responseDeserializeOriginal = method.responseDeserialize;
68
+
69
+ // Replace default serialisation with NOP, since when tunnelling we encrypt the payload
70
+ // before sending it to the channel, so we want it to be treated as a pure byte array (pass-through).
71
+ method.requestSerialize = serialiseNOP;
72
+ method.responseDeserialize = serialiseNOP;
73
+ }
74
+ });
75
+ }
76
+
77
+ const SubClass = grpc.makeGenericClientConstructor(serviceDescriptors);
78
+
79
+ return new SubClass(address, grpcCredentials);
80
80
  }
81
81
 
82
82
  /**
@@ -84,150 +84,150 @@ export function createClient(
84
84
  * @param {*} api
85
85
  */
86
86
  const grpcWrap = (api) => {
87
- const grpcRoutes = {};
88
-
89
- // Don't bother trying to wrap class constructors.
90
- const ignoreMethods = ["constructor"];
91
-
92
- // Support class instances and PoJos
93
- const isClass = api.constructor !== Object;
94
- const iterate = Object.getOwnPropertyNames(
95
- isClass ? api.constructor.prototype : api,
96
- );
97
-
98
- // Wrap each method on the api.
99
- forEach(iterate, (name) => {
100
- if (!ignoreMethods.includes(name)) {
101
- const method = api[name];
102
- grpcRoutes[name] = (call, callback) => {
103
- // Execute the method in the context of the api.
104
- const resultPromise = method.call(api, call.request, call);
105
-
106
- // Enforce promise results.
107
- if (!(resultPromise?.then && resultPromise?.catch)) {
108
- throw new Error(
109
- `implementation methods must return a Promise [${name}]`,
110
- );
111
- }
112
-
113
- resultPromise
114
- .then((response) => {
115
- if (callback) {
116
- callback(null, response);
117
- }
118
- })
119
- .catch((err) => {
120
- if (callback) {
121
- callback(err);
122
- } else {
123
- // TODO - verify this => how to send error to stream clients?
124
- // Destroy stream on error.
125
- call.destroy(err);
126
- }
127
- });
128
- };
129
- }
130
- });
131
-
132
- return grpcRoutes;
87
+ const grpcRoutes = {};
88
+
89
+ // Don't bother trying to wrap class constructors.
90
+ const ignoreMethods = ["constructor"];
91
+
92
+ // Support class instances and PoJos
93
+ const isClass = api.constructor !== Object;
94
+ const iterate = Object.getOwnPropertyNames(
95
+ isClass ? api.constructor.prototype : api,
96
+ );
97
+
98
+ // Wrap each method on the api.
99
+ forEach(iterate, (name) => {
100
+ if (!ignoreMethods.includes(name)) {
101
+ const method = api[name];
102
+ grpcRoutes[name] = (call, callback) => {
103
+ // Execute the method in the context of the api.
104
+ const resultPromise = method.call(api, call.request, call);
105
+
106
+ // Enforce promise results.
107
+ if (!(resultPromise?.then && resultPromise?.catch)) {
108
+ throw new Error(
109
+ `implementation methods must return a Promise [${name}]`,
110
+ );
111
+ }
112
+
113
+ resultPromise
114
+ .then((response) => {
115
+ if (callback) {
116
+ callback(null, response);
117
+ }
118
+ })
119
+ .catch((err) => {
120
+ if (callback) {
121
+ callback(err);
122
+ } else {
123
+ // TODO - verify this => how to send error to stream clients?
124
+ // Destroy stream on error.
125
+ call.destroy(err);
126
+ }
127
+ });
128
+ };
129
+ }
130
+ });
131
+
132
+ return grpcRoutes;
133
133
  };
134
134
 
135
135
  class GrpcServer {
136
- constructor(options) {
137
- const { grpc, serviceDescriptors, routes, tlsOptions, requireClientCert } =
138
- options;
139
- this._grpc = grpc;
140
- this._server = new this._grpc.Server({
141
- "grpc.keepalive_time_ms": 500,
142
- "grpc.keepalive_timeout_ms": 5000,
143
- "grpc.http2.max_pings_without_data": 0,
144
- "grpc.keepalive_permit_without_calls": 1,
145
- "grpc.http2.min_ping_interval_without_data_ms": 250,
146
- "grpc.http2.max_ping_strikes": 0,
147
- });
148
-
149
- if (tlsOptions) {
150
- const caList = [];
151
- const certChains = [];
152
- [].concat(tlsOptions).forEach((tlsOption) => {
153
- caList.push(tlsOption.ca);
154
- certChains.push({
155
- private_key: tlsOption.key,
156
- cert_chain: tlsOption.cert,
157
- });
158
- });
159
-
160
- // Create credentials from the volt CA and our own pub/priv key.
161
- this._credentials = grpc.ServerCredentials.createSsl(
162
- // The root CAs we trust when verifying client certificates.
163
- Buffer.concat(caList),
164
- // The certificate chain(s) we present to the client.
165
- certChains,
166
- // Flag indicating if we insist on a client certificate.
167
- requireClientCert,
168
- );
169
- } else {
170
- this._credentials = grpc.ServerCredentials.createInsecure();
171
- }
172
-
173
- if (serviceDescriptors && !isEmpty(routes)) {
174
- // Enforce decoupling of the service implementation.
175
- const router = grpcWrap(routes);
176
- this._server.addService(serviceDescriptors, router);
177
- }
178
- }
179
-
180
- close(force = false) {
181
- return new Promise((resolve) => {
182
- if (force) {
183
- resolve(this._server.forceShutdown());
184
- } else {
185
- this._server.tryShutdown(resolve);
186
- }
187
- });
188
- }
189
-
190
- listen(address) {
191
- return new Promise((resolve, reject) => {
192
- // If no port is specified, grpc will return the port number assigned.
193
- this._server.bindAsync(address, this._credentials, (err, port) => {
194
- if (err) {
195
- reject(err);
196
- } else {
197
- this._port = port;
198
- this._server.start();
199
- resolve(port);
200
- }
201
- });
202
- });
203
- }
204
-
205
- get server() {
206
- return this._server;
207
- }
208
-
209
- get port() {
210
- return this._port;
211
- }
136
+ constructor(options) {
137
+ const { grpc, serviceDescriptors, routes, tlsOptions, requireClientCert } =
138
+ options;
139
+ this._grpc = grpc;
140
+ this._server = new this._grpc.Server({
141
+ "grpc.keepalive_time_ms": 500,
142
+ "grpc.keepalive_timeout_ms": 5000,
143
+ "grpc.http2.max_pings_without_data": 0,
144
+ "grpc.keepalive_permit_without_calls": 1,
145
+ "grpc.http2.min_ping_interval_without_data_ms": 250,
146
+ "grpc.http2.max_ping_strikes": 0,
147
+ });
148
+
149
+ if (tlsOptions) {
150
+ const caList = [];
151
+ const certChains = [];
152
+ [].concat(tlsOptions).forEach((tlsOption) => {
153
+ caList.push(tlsOption.ca);
154
+ certChains.push({
155
+ private_key: tlsOption.key,
156
+ cert_chain: tlsOption.cert,
157
+ });
158
+ });
159
+
160
+ // Create credentials from the volt CA and our own pub/priv key.
161
+ this._credentials = grpc.ServerCredentials.createSsl(
162
+ // The root CAs we trust when verifying client certificates.
163
+ Buffer.concat(caList),
164
+ // The certificate chain(s) we present to the client.
165
+ certChains,
166
+ // Flag indicating if we insist on a client certificate.
167
+ requireClientCert,
168
+ );
169
+ } else {
170
+ this._credentials = grpc.ServerCredentials.createInsecure();
171
+ }
172
+
173
+ if (serviceDescriptors && !isEmpty(routes)) {
174
+ // Enforce decoupling of the service implementation.
175
+ const router = grpcWrap(routes);
176
+ this._server.addService(serviceDescriptors, router);
177
+ }
178
+ }
179
+
180
+ close(force = false) {
181
+ return new Promise((resolve) => {
182
+ if (force) {
183
+ resolve(this._server.forceShutdown());
184
+ } else {
185
+ this._server.tryShutdown(resolve);
186
+ }
187
+ });
188
+ }
189
+
190
+ listen(address) {
191
+ return new Promise((resolve, reject) => {
192
+ // If no port is specified, grpc will return the port number assigned.
193
+ this._server.bindAsync(address, this._credentials, (err, port) => {
194
+ if (err) {
195
+ reject(err);
196
+ } else {
197
+ this._port = port;
198
+ this._server.start();
199
+ resolve(port);
200
+ }
201
+ });
202
+ });
203
+ }
204
+
205
+ get server() {
206
+ return this._server;
207
+ }
208
+
209
+ get port() {
210
+ return this._port;
211
+ }
212
212
  }
213
213
 
214
214
  export function createServer(
215
- grpc,
216
- serviceDescriptors,
217
- routes,
218
- cryptoCache,
219
- requireClientCert,
215
+ grpc,
216
+ serviceDescriptors,
217
+ routes,
218
+ cryptoCache,
219
+ requireClientCert,
220
220
  ) {
221
- let tlsOptions;
222
- if (cryptoCache) {
223
- tlsOptions = createSecureContextOptions(cryptoCache);
224
- }
225
- const serverArgs = {
226
- grpc,
227
- serviceDescriptors,
228
- routes,
229
- tlsOptions,
230
- requireClientCert,
231
- };
232
- return new GrpcServer(serverArgs);
221
+ let tlsOptions;
222
+ if (cryptoCache) {
223
+ tlsOptions = createSecureContextOptions(cryptoCache);
224
+ }
225
+ const serverArgs = {
226
+ grpc,
227
+ serviceDescriptors,
228
+ routes,
229
+ tlsOptions,
230
+ requireClientCert,
231
+ };
232
+ return new GrpcServer(serverArgs);
233
233
  }
@@ -9,90 +9,90 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
9
9
  const defaultProtoPath = join(__dirname, "../protobuf");
10
10
 
11
11
  const defaultLoaderOptions = {
12
- keepCase: true,
13
- longs: String,
14
- enums: String,
15
- defaults: true,
16
- oneofs: true,
17
- includeDirs: [defaultProtoPath],
12
+ keepCase: true,
13
+ longs: String,
14
+ enums: String,
15
+ defaults: true,
16
+ oneofs: true,
17
+ includeDirs: [defaultProtoPath],
18
18
  };
19
19
 
20
20
  const getProtoDescriptors = (grpc, protoPath, opts) => {
21
- const definition = protoLoader.loadSync(
22
- protoPath,
23
- opts || defaultLoaderOptions,
24
- );
25
- const descriptors = grpc.loadPackageDefinition(definition);
26
- return descriptors;
21
+ const definition = protoLoader.loadSync(
22
+ protoPath,
23
+ opts || defaultLoaderOptions,
24
+ );
25
+ const descriptors = grpc.loadPackageDefinition(definition);
26
+ return descriptors;
27
27
  };
28
28
 
29
29
  const getProtoService = (descriptors, servicePath) => {
30
- const serviceDef = getProp(descriptors, servicePath);
31
- if (serviceDef?.service) {
32
- return serviceDef.service;
33
- } else {
34
- return null;
35
- }
30
+ const serviceDef = getProp(descriptors, servicePath);
31
+ if (serviceDef?.service) {
32
+ return serviceDef.service;
33
+ } else {
34
+ return null;
35
+ }
36
36
  };
37
37
 
38
38
  function getServiceDescriptorsFromPath(
39
- grpc,
40
- protoPath,
41
- servicePackageName,
42
- opts,
39
+ grpc,
40
+ protoPath,
41
+ servicePackageName,
42
+ opts,
43
43
  ) {
44
- let serviceDescriptors;
45
- const descriptors = getProtoDescriptors(grpc, protoPath, opts);
46
- if (descriptors) {
47
- serviceDescriptors = getProtoService(descriptors, servicePackageName);
48
- }
44
+ let serviceDescriptors;
45
+ const descriptors = getProtoDescriptors(grpc, protoPath, opts);
46
+ if (descriptors) {
47
+ serviceDescriptors = getProtoService(descriptors, servicePackageName);
48
+ }
49
49
 
50
- return serviceDescriptors;
50
+ return serviceDescriptors;
51
51
  }
52
52
 
53
53
  function getServiceDescriptors(grpc, servicePackageName, opts) {
54
- // Extract the package components of the service proto package. The package name should be of the
55
- // fully qualified proto path, e.g. tdx.volt_api.webcam.v1.WebcamControlAPI.
56
- const protoServiceComponents = servicePackageName.split(".");
54
+ // Extract the package components of the service proto package. The package name should be of the
55
+ // fully qualified proto path, e.g. tdx.volt_api.webcam.v1.WebcamControlAPI.
56
+ const protoServiceComponents = servicePackageName.split(".");
57
57
 
58
- // The service name is the last component of the path.
59
- const protoServiceName = protoServiceComponents.pop();
58
+ // The service name is the last component of the path.
59
+ const protoServiceName = protoServiceComponents.pop();
60
60
 
61
- // The actual file name should match the snake case of the service name.
62
- const protoFileName = `${snakeCase(protoServiceName).toLowerCase()}.proto`;
61
+ // The actual file name should match the snake case of the service name.
62
+ const protoFileName = `${snakeCase(protoServiceName).toLowerCase()}.proto`;
63
63
 
64
- // The path to the proto file should match each component of the package name.
65
- const protoPath = join(
66
- defaultProtoPath,
67
- ...protoServiceComponents,
68
- protoFileName,
69
- );
64
+ // The path to the proto file should match each component of the package name.
65
+ const protoPath = join(
66
+ defaultProtoPath,
67
+ ...protoServiceComponents,
68
+ protoFileName,
69
+ );
70
70
 
71
- let serviceDescriptors;
72
- const descriptors = getProtoDescriptors(grpc, protoPath, opts);
73
- if (descriptors) {
74
- serviceDescriptors = getProtoService(descriptors, servicePackageName);
75
- }
71
+ let serviceDescriptors;
72
+ const descriptors = getProtoDescriptors(grpc, protoPath, opts);
73
+ if (descriptors) {
74
+ serviceDescriptors = getProtoService(descriptors, servicePackageName);
75
+ }
76
76
 
77
- return serviceDescriptors;
77
+ return serviceDescriptors;
78
78
  }
79
79
 
80
80
  function getProtoDescriptorMethods(protoDescriptor) {
81
- const methods = [];
82
- Object.keys(protoDescriptor).forEach((methodName) => {
83
- const method = protoDescriptor[methodName];
84
- methods.push({
85
- path: method.path,
86
- client_streaming: method.requestStream,
87
- server_streaming: method.responseStream,
88
- });
89
- });
90
- return methods;
81
+ const methods = [];
82
+ Object.keys(protoDescriptor).forEach((methodName) => {
83
+ const method = protoDescriptor[methodName];
84
+ methods.push({
85
+ path: method.path,
86
+ client_streaming: method.requestStream,
87
+ server_streaming: method.responseStream,
88
+ });
89
+ });
90
+ return methods;
91
91
  }
92
92
 
93
93
  export {
94
- getServiceDescriptors,
95
- getProtoDescriptors,
96
- getProtoDescriptorMethods,
97
- getServiceDescriptorsFromPath,
94
+ getServiceDescriptors,
95
+ getProtoDescriptors,
96
+ getProtoDescriptorMethods,
97
+ getServiceDescriptorsFromPath,
98
98
  };