@nsshunt/stsappframework 2.19.205 → 2.19.206

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.
Files changed (56) hide show
  1. package/dist/masterprocessbase.js +18 -68
  2. package/dist/masterprocessbase.js.map +1 -1
  3. package/dist/processbase.js +59 -1
  4. package/dist/processbase.js.map +1 -1
  5. package/dist/processoptions.js +9 -0
  6. package/dist/processoptions.js.map +1 -1
  7. package/dist/serverprocessbase.js +342 -0
  8. package/dist/serverprocessbase.js.map +1 -0
  9. package/dist/singleprocessbase.js +13 -220
  10. package/dist/singleprocessbase.js.map +1 -1
  11. package/dist/tcpclient/app.js +21 -0
  12. package/dist/tcpclient/app.js.map +1 -0
  13. package/dist/tcpclient/app2.js +55 -0
  14. package/dist/tcpclient/app2.js.map +1 -0
  15. package/dist/tcpclientserver/app.js +15 -0
  16. package/dist/tcpclientserver/app.js.map +1 -0
  17. package/dist/tcpclientserver/appConfig.js +61 -0
  18. package/dist/tcpclientserver/appConfig.js.map +1 -0
  19. package/dist/workerprocessbase.js +15 -224
  20. package/dist/workerprocessbase.js.map +1 -1
  21. package/package.json +3 -2
  22. package/run.sh +24 -0
  23. package/runc1.sh +24 -0
  24. package/runc2.sh +24 -0
  25. package/src/commonTypes.ts +1 -1
  26. package/src/masterprocessbase.ts +11 -66
  27. package/src/processbase.ts +62 -2
  28. package/src/processoptions.ts +9 -9
  29. package/src/serverprocessbase.ts +390 -0
  30. package/src/singleprocessbase.ts +20 -254
  31. package/src/tcpclient/app.ts +19 -0
  32. package/src/tcpclient/app2.ts +55 -0
  33. package/src/tcpclientserver/app.ts +9 -0
  34. package/src/tcpclientserver/appConfig.ts +66 -0
  35. package/src/workerprocessbase.ts +24 -251
  36. package/types/commonTypes.d.ts +1 -1
  37. package/types/commonTypes.d.ts.map +1 -1
  38. package/types/masterprocessbase.d.ts.map +1 -1
  39. package/types/processbase.d.ts +4 -0
  40. package/types/processbase.d.ts.map +1 -1
  41. package/types/processoptions.d.ts +8 -8
  42. package/types/processoptions.d.ts.map +1 -1
  43. package/types/serverprocessbase.d.ts +21 -0
  44. package/types/serverprocessbase.d.ts.map +1 -0
  45. package/types/singleprocessbase.d.ts +3 -7
  46. package/types/singleprocessbase.d.ts.map +1 -1
  47. package/types/tcpclient/app.d.ts +2 -0
  48. package/types/tcpclient/app.d.ts.map +1 -0
  49. package/types/tcpclient/app2.d.ts +2 -0
  50. package/types/tcpclient/app2.d.ts.map +1 -0
  51. package/types/tcpclientserver/app.d.ts +2 -0
  52. package/types/tcpclientserver/app.d.ts.map +1 -0
  53. package/types/tcpclientserver/appConfig.d.ts +3 -0
  54. package/types/tcpclientserver/appConfig.d.ts.map +1 -0
  55. package/types/workerprocessbase.d.ts +2 -7
  56. package/types/workerprocessbase.d.ts.map +1 -1
package/runc2.sh ADDED
@@ -0,0 +1,24 @@
1
+ #!/bin/sh
2
+ # openssl req -nodes -new -x509 -keyout server.key -out server.cert
3
+ clear; \
4
+ export STS_PROJ_ROOT=./..; \
5
+ STSENVFILE=$STS_PROJ_ROOT/stsglobalresources/.env \
6
+ DB_SCRIPT_FOLDER=$STS_PROJ_ROOT/stsglobalresources/db-scripts \
7
+ REST01_PORT=3003 \
8
+ REST01_HOST_PORT=3003 \
9
+ REST01_SERVICE_NAME="STSRest01-3003" \
10
+ REST01_API_IDENTIFIER="https://stsmda.com.au/stsrest01api/v1.0/" \
11
+ REST01_ENDPOINT="https://stsrest.stsmda.org" \
12
+ MAX_CPU=1 \
13
+ AS_ENDPOINT=https://stscore.stsmda.org \
14
+ AS_HOST_PORT=3002 \
15
+ AS_PORT=3002 \
16
+ DB_HOST=localhost \
17
+ DB_PORT=5432 \
18
+ DB_PASSWORD=postgres \
19
+ HTTPS_SERVER_KEY_PATH=/etc/letsencrypt/live/stsmda.org/privkey.pem \
20
+ HTTPS_SERVER_CERT_PATH=/etc/letsencrypt/live/stsmda.org/fullchain.pem \
21
+ DEBUG=proc* \
22
+ PUBLISH_DEBUG=false \
23
+ UV_THREADPOOL_SIZE=64 \
24
+ node dist/tcpclient/app2
@@ -70,7 +70,7 @@ export interface ISingleProcessBase extends IProcessBase {
70
70
  get expressServer(): any // STSExpressServer - need to put interface in common types - circular reference
71
71
  SetupServer(): Promise<boolean>
72
72
  SetupServerEx: () => Promise<void>
73
- Terminate(): Promise<void>
73
+ TerminateApplication(): Promise<void>
74
74
  }
75
75
 
76
76
  export interface IWorkerProcessBase extends IProcessBase {
@@ -7,7 +7,6 @@ import axios from 'axios';
7
7
  import cluster, { Worker } from 'node:cluster'
8
8
 
9
9
  import os from 'os';
10
- let numCPUs = os.cpus().length;
11
10
 
12
11
  import colors from 'colors'
13
12
 
@@ -26,7 +25,7 @@ import debugModule from 'debug'
26
25
  import { Gauge, GaugeTypes, InstrumentGaugeTelemetry, InstrumentGaugeOptions, InstrumentHistogramTelemetry } from '@nsshunt/stsinstrumentation'
27
26
  import { GetFirstNetworkInterface } from './network'
28
27
 
29
- import { ProcessOptions } from './processoptions'
28
+ import { ProcessOptions, STSServerType } from './processoptions'
30
29
  import { ProcessBase } from './processbase';
31
30
  import { IMasterProcessBase } from './commonTypes';
32
31
 
@@ -120,15 +119,19 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
120
119
  this.#metricsServer = express();
121
120
  this.#aggregatorRegistry = new AggregatorRegistry();
122
121
 
123
- if (this.options.httpsServer === true) {
122
+ switch (this.options.serverType) {
123
+ case STSServerType.EXPRESS_TLS : {
124
124
  const options = {
125
125
  key: fs.readFileSync(this.options.httpsServerKeyPath),
126
126
  cert: fs.readFileSync(this.options.httpsServerCertificatePath)
127
127
  };
128
128
  this.#httpServer = createServerHttps(options, this.#metricsServer);
129
- } else {
129
+ }
130
+ break;
131
+ case STSServerType.EXPRESS : {
130
132
  this.#httpServer = createServer(this.#metricsServer);
131
133
  }
134
+ }
132
135
  //this.#httpServer.maxConnections = 50;
133
136
 
134
137
  this.#metricsServer.get('/cluster_metrics', async (req: any, res: any) => {
@@ -388,49 +391,9 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
388
391
  this.ProcessStartup();
389
392
  const LogEx = this.LogEx;
390
393
 
391
- /*
392
- if (this.instruments !== null) {
393
- this.instruments[Gauge.LOGGER].consoleLogging = this.options.consoleLogging;
394
- this.instruments[Gauge.LOGGER].instrumentLogging = this.options.instrumentLogging;
395
- }
396
- */
397
-
398
- // https://systeminformation.io/
399
- const valueObject = {
400
- system: '*',
401
- osInfo: '*',
402
- cpu: '*',
403
- mem: '*'
404
- }
405
-
406
- const sysinfo = await si.get(valueObject);
407
-
408
- if (goptions.useCPUs > 0) {
409
- if (goptions.useCPUs >= 1) {
410
- numCPUs = goptions.useCPUs;
411
- } else {
412
- numCPUs = Math.round(sysinfo.cpu.cores * goptions.useCPUs);
413
- }
414
- } else {
415
- numCPUs = sysinfo.cpu.physicalCores;
416
- }
417
-
418
394
  LogEx(`Service instance starting. Instance Id: [${this.options.serviceInstanceId}]`);
419
395
 
420
- const hostaddr = GetFirstNetworkInterface();
421
- if (hostaddr !== null)
422
- {
423
- LogEx(`Host Address: ${hostaddr}`);
424
- } else {
425
- LogEx(`Unknown Host Address.`);
426
- }
427
- LogEx(`Server starting with ${numCPUs} Cores/Threads`);
428
-
429
- LogEx(`Hostname: ${sysinfo.osInfo.hostname}`);
430
- LogEx(`System: ${JSON.stringify(sysinfo.system)}`);
431
- LogEx(`OS Info: ${JSON.stringify(sysinfo.osInfo)}`);
432
- LogEx(`CPU: ${JSON.stringify(sysinfo.cpu)}`);
433
- LogEx(`Memory: ${JSON.stringify(sysinfo.mem)}`);
396
+ this.LogSystemTelemetry();
434
397
 
435
398
  // socket.io
436
399
  // setup connections between the workers
@@ -447,18 +410,13 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
447
410
  this.#SetupPrometheusForMaster(LogEx);
448
411
  }
449
412
 
413
+ const numCPUs = await this.GetNumCPUs();
450
414
  for (let i=0; i < numCPUs; i++) {
451
415
  this.#SpawnWorker();
452
416
  }
453
417
 
454
418
  this.#InitCluster(LogEx);
455
419
 
456
- /*
457
- if (this.publishBroker !== null) {
458
- this.publishBroker.StartPublish();
459
- }
460
- */
461
-
462
420
  cluster.on('listening', (worker, address) =>
463
421
  {
464
422
  LogEx(`Worker process ${worker.process.pid} is listening at address: ${JSON.stringify(address)}`);
@@ -494,19 +452,6 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
494
452
  }
495
453
  });
496
454
 
497
-
498
- const GetSignalColour = (signal: any) =>
499
- {
500
- let msgcolor = null;
501
- if (signal && signal === 'SIGINT')
502
- {
503
- msgcolor = colors.yellow;
504
- } else {
505
- msgcolor = colors.red;
506
- }
507
- return msgcolor;
508
- };
509
-
510
455
  // Terminate in order;
511
456
  // forked worker threads (send signal)
512
457
  // De-Register service
@@ -534,9 +479,9 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
534
479
  }
535
480
 
536
481
  if (signal) {
537
- LogEx(GetSignalColour(signal)(`Main Process (masterprocess): ${process.pid} received signal: ${signal}`));
482
+ LogEx(this.GetSignalColour(signal)(`Main Process (masterprocess): ${process.pid} received signal: ${signal}`));
538
483
  } else {
539
- LogEx(GetSignalColour(null)(`Main Process (masterprocess): ${process.pid} received Terminate without signal.`));
484
+ LogEx(this.GetSignalColour(null)(`Main Process (masterprocess): ${process.pid} received Terminate without signal.`));
540
485
  }
541
486
 
542
487
  LogEx(`De-Registering service.`);
@@ -3,6 +3,8 @@ import { memoryUsage, exit } from 'process'
3
3
  import path from 'node:path';
4
4
  import cluster from 'node:cluster'
5
5
  import colors from 'colors'
6
+ import si from 'systeminformation' // https://systeminformation.io/
7
+ import { GetFirstNetworkInterface } from './network'
6
8
 
7
9
  import debugModule from 'debug'
8
10
  const debug = debugModule(`proc:${process.pid}:processBase`);
@@ -14,7 +16,7 @@ import { PGAccessLayer, PGPoolManager, PGPoolManagerEventName } from '@nsshunt/s
14
16
  import { $Options } from '@nsshunt/stsconfig'
15
17
  const goptions = $Options()
16
18
 
17
- import { ProcessOptions } from './processoptions'
19
+ import { ProcessOptions, STSServerType } from './processoptions'
18
20
  import { STSOptionsBase, JSONObject } from '@nsshunt/stsutils'
19
21
  import { StatusCodes } from 'http-status-codes'
20
22
 
@@ -162,7 +164,7 @@ export abstract class ProcessBase extends STSOptionsBase implements IProcessBase
162
164
  payloadType: InstrumentPayloadType.service,
163
165
  consoleLogging: this.options.consoleLogging,
164
166
  instrumentLogging: this.options.instrumentLogging,
165
- httpServer: this.options.httpServer,
167
+ httpServer: (this.options.serverType === STSServerType.EXPRESS),
166
168
  instrumentationObservationInterval: this.options.instrumentationObservationInterval,
167
169
  instrumentationTimeWindow: this.options.instrumentationTimeWindow,
168
170
  instrumentDefinitions: this.GetAdditionalInstruments(),
@@ -325,4 +327,62 @@ export abstract class ProcessBase extends STSOptionsBase implements IProcessBase
325
327
  }
326
328
  return null;
327
329
  }
330
+
331
+ GetNumCPUs = async (): Promise<number> => {
332
+ // https://systeminformation.io/
333
+ const valueObject = {
334
+ cpu: '*'
335
+ }
336
+
337
+ const sysinfo = await si.get(valueObject);
338
+ let numCPUs = 2;
339
+ if (goptions.useCPUs > 0) {
340
+ if (goptions.useCPUs >= 1) {
341
+ numCPUs = goptions.useCPUs;
342
+ } else {
343
+ numCPUs = Math.round(sysinfo.cpu.cores * goptions.useCPUs);
344
+ }
345
+ } else {
346
+ numCPUs = sysinfo.cpu.physicalCores;
347
+ }
348
+ return numCPUs;
349
+ }
350
+
351
+ LogSystemTelemetry = async () => {
352
+ // https://systeminformation.io/
353
+ const valueObject = {
354
+ system: '*',
355
+ osInfo: '*',
356
+ cpu: '*',
357
+ mem: '*'
358
+ }
359
+
360
+ const sysinfo = await si.get(valueObject);
361
+ const numCPUs = await this.GetNumCPUs();
362
+ const hostname = sysinfo.osInfo.hostname;
363
+
364
+ const hostaddr = GetFirstNetworkInterface();
365
+ if (hostaddr !== null) {
366
+ this.LogEx(`Host Address: ${hostaddr}`);
367
+ } else {
368
+ this.LogEx(`Unknown Host Address.`);
369
+ }
370
+ this.LogEx(`Server starting with ${numCPUs} Cores/Threads`);
371
+
372
+ this.LogEx(`Hostname: ${hostname}`);
373
+ this.LogEx(`System: ${JSON.stringify(sysinfo.system)}`);
374
+ this.LogEx(`OS Info: ${JSON.stringify(sysinfo.osInfo)}`);
375
+ this.LogEx(`CPU: ${JSON.stringify(sysinfo.cpu)}`);
376
+ this.LogEx(`Memory: ${JSON.stringify(sysinfo.mem)}`);
377
+ }
378
+
379
+ GetSignalColour = (signal: any) => {
380
+ let msgcolor = null;
381
+ if (signal === 'SIGINT') {
382
+ msgcolor = colors.yellow;
383
+ } else {
384
+ msgcolor = colors.red;
385
+ }
386
+ return msgcolor;
387
+ };
328
388
  }
@@ -13,6 +13,14 @@ export interface STSExpressServer {
13
13
  */
14
14
  export type ExpressRouteFactory = (app: express.Express, stsApp: IProcessBase) => void
15
15
 
16
+ export enum STSServerType {
17
+ NONE = "NONE",
18
+ EXPRESS = "EXPRESS",
19
+ EXPRESS_TLS = "EXPRESS_TLS",
20
+ TCPRAW_TLS = "TCPRAW_TLS",
21
+ JSONRPC2_TLS = "JSONRPC_TLS"
22
+ }
23
+
16
24
  //export interface ProcessOptions extends JSONObject {
17
25
  export interface ProcessOptions {
18
26
  /**
@@ -27,15 +35,7 @@ export interface ProcessOptions {
27
35
 
28
36
  // Service details
29
37
 
30
- /**
31
- * Does this service run an HTTP server.
32
- */
33
- httpServer: boolean
34
-
35
- /**
36
- * Does this service run an HTTPS server. Note that httpServer must also be true if this value is true.
37
- */
38
- httpsServer: boolean
38
+ serverType: STSServerType
39
39
 
40
40
  /**
41
41
  * HTTPS service public key path.
@@ -0,0 +1,390 @@
1
+ import debugModule from 'debug'
2
+ const debug = debugModule(`proc:${process.pid}`);
3
+
4
+ import { $Options } from '@nsshunt/stsconfig'
5
+ const goptions = $Options()
6
+
7
+ import fs from "fs"
8
+ import colors from 'colors'
9
+
10
+ import { createAdapter as clusterCreateAdapter } from '@socket.io/cluster-adapter'
11
+ import { createAdapter } from "@socket.io/redis-streams-adapter";
12
+
13
+ import { Gauge, InstrumentGaugeTelemetry } from '@nsshunt/stsinstrumentation'
14
+ import { JSONObject, Sleep } from '@nsshunt/stsutils'
15
+
16
+ import { ProcessOptions, STSServerType } from './processoptions'
17
+ import { ProcessBase } from './processbase';
18
+
19
+ import { register, Counter, collectDefaultMetrics, AggregatorRegistry } from 'prom-client'
20
+
21
+ import { createServer as createServerHttps } from 'https'
22
+ import { createServer } from 'http'
23
+ import tls from 'node:tls'
24
+ import net from 'node:net'
25
+
26
+ import { Server, ServerOptions } from "socket.io";
27
+
28
+ import { v4 as uuidv4 } from 'uuid';
29
+
30
+ import { IPCMessage, IPCMessages, IPCMessagePayload, IPCMessageCommand, IWorkerProcessBase, ISocketIoHelper } from './commonTypes'
31
+ import { STSExpressServer } from './server';
32
+ import { Express } from 'express'
33
+
34
+ import { createClient, RedisClientType } from 'redis';
35
+
36
+ import jayson from 'jayson'
37
+
38
+ /**
39
+ * todo
40
+ * @typedef {Object} options - todo
41
+ * @property {boolean} [wssServer=false] - Create a web socket server on this worker instance
42
+ */
43
+ export class ServerProcessBase extends ProcessBase
44
+ {
45
+ #masterProcessExitTime = goptions.masterProcessExitTime;
46
+ #io: Server | null = null;
47
+ #redisClient: RedisClientType | null = null;
48
+ #httpServer: any = null;
49
+ #expressServer: STSExpressServer | null = null;
50
+ #sockets: net.Socket[] = [ ];
51
+ #shuttingDown = false;
52
+
53
+ constructor(options: ProcessOptions) {
54
+ super(options);
55
+ }
56
+
57
+ get httpServer() {
58
+ return this.#httpServer;
59
+ }
60
+
61
+ get io() {
62
+ return this.#io;
63
+ }
64
+
65
+ get expressServer(): STSExpressServer | null {
66
+ return this.#expressServer;
67
+ }
68
+ set expressServer(val: STSExpressServer | null) {
69
+ this.#expressServer = val;
70
+ }
71
+
72
+ // Setup server to Prometheus scrapes:
73
+ #SetupPrometheusEndPoints = (expressServer: Express) => {
74
+ // AggregatorRegistry is required here in the worker as well as the master in order for prom-client to work correctly.
75
+ new AggregatorRegistry();
76
+
77
+ const prefix = 'sts_';
78
+
79
+ collectDefaultMetrics({
80
+ labels: { NODE_APP_INSTANCE: process.pid },
81
+ prefix: prefix
82
+ });
83
+
84
+ const c = new Counter({
85
+ name: 'sts_test_counter',
86
+ help: 'Example of a counter',
87
+ labelNames: ['code'],
88
+ });
89
+
90
+ setInterval(() => {
91
+ c.inc({ code: 200 });
92
+ }, 1000).unref();
93
+
94
+ setInterval(() => {
95
+ c.inc({ code: 400 });
96
+ c.inc({ code: 'worker_' + process.pid });
97
+ }, 500).unref();
98
+
99
+ expressServer.get('/metrics', async (req: any, res: any) => {
100
+ try {
101
+ res.set('Content-Type', register.contentType);
102
+ res.end(await register.metrics());
103
+ } catch (ex) {
104
+ res.status(500).end(ex);
105
+ }
106
+ });
107
+
108
+ expressServer.get('/metrics/counter', async (req: any, res: any) => {
109
+ try {
110
+ res.set('Content-Type', register.contentType);
111
+ res.end(await register.getSingleMetricAsString('test_counter'));
112
+ } catch (ex) {
113
+ res.status(500).end(ex);
114
+ }
115
+ });
116
+ }
117
+
118
+ #SetupTLSServer = async (socket: net.Socket): Promise<void> => {
119
+ // Add a 'close' event handler to this instance of socket
120
+ console.log('CONNECTED: ' + socket.remoteAddress + ':' + socket.remotePort + ' ' + process.pid);
121
+ this.#sockets.push(socket);
122
+
123
+ const self = this;
124
+ socket.on('close', function(data: any) {
125
+ const index = self.#sockets.findIndex(function(o) {
126
+ return o.remoteAddress === socket.remoteAddress && o.remotePort === socket.remotePort;
127
+ })
128
+ if (index !== -1) self.#sockets.splice(index, 1);
129
+ console.log('CLOSED: ' + socket.remoteAddress + ' ' + socket.remotePort + ' ' + process.pid);
130
+ });
131
+
132
+ socket.on('data', function(data: any) {
133
+ console.log('DATA ' + socket.remoteAddress + ': ' + socket.remotePort + ': ' + data);
134
+ socket.write(socket.remoteAddress + ':' + socket.remotePort + " said " + data + '\n');
135
+
136
+ // Write the data back to all the connected, the client will receive it as data from the server
137
+ /*
138
+ self.#sockets.forEach(function(socket, index, array) {
139
+ socket.write(socket.remoteAddress + ':' + socket.remotePort + " said " + data + '\n');
140
+ });
141
+
142
+ */
143
+ });
144
+ }
145
+
146
+ #SetupRPCServer = async (socket: net.Socket): Promise<void> => {
147
+ console.log('CONNECTED: ' + socket.remoteAddress + ':' + socket.remotePort + ' ' + process.pid);
148
+
149
+ socket.on('close', function(data: any) {
150
+ console.log('CLOSED: ' + socket.remoteAddress + ' ' + socket.remotePort + ' ' + process.pid);
151
+ });
152
+
153
+ socket.on('data', function(data: any) {
154
+ console.log('DATA ' + socket.remoteAddress + ': ' + data);
155
+ });
156
+ }
157
+
158
+ #SetupWSSServer = async (clusterMode: boolean): Promise<void> => {
159
+ // socket.io
160
+ // WebSocket
161
+ const options: Partial<ServerOptions> = {
162
+ transports: [ "websocket" ] // or [ "websocket", "polling" ] (to use long-poolling. Note that the order matters)
163
+ // The default path is /socket.io
164
+ // This can be changed with the path option as shown below
165
+ //,path: '/zzz'
166
+ };
167
+
168
+ //this.#io = require("socket.io")(this.#httpServer, options);
169
+ this.#io = new Server(this.#httpServer, options);
170
+
171
+ if (clusterMode) {
172
+ if (this.options.useRedisAdaptor) {
173
+ this.LogEx(`Using Redis for socket.io cluster management (worker)`);
174
+ if (this.options.redisAdaptorUrl) {
175
+ this.LogEx(`Redis url: [${this.options.redisAdaptorUrl}]`);
176
+ this.#redisClient = createClient({url: this.options.redisAdaptorUrl});
177
+ } else {
178
+ this.LogEx(`Redis url: [localhost]`);
179
+ this.#redisClient = createClient();
180
+ }
181
+ await this.#redisClient.connect();
182
+ this.#io.adapter(createAdapter(this.#redisClient) as any);
183
+ this.LogEx(`Redis successfully connected.`);
184
+ } else {
185
+ this.#io.adapter(clusterCreateAdapter() as any);
186
+ this.LogEx(`Using nodejs cluster mode for socket.io cluster management`);
187
+ }
188
+ } else {
189
+ this.LogEx(`Not using any adaptors for socket.io cluster management.}`);
190
+ }
191
+
192
+ // To use a seperate socket server, the code below can be applied.
193
+ // this.#io = require("socket.io")(options);
194
+ // this.#io.adapter(createAdapter());
195
+ // this.#io.listen(3006);
196
+ // LogEx(`socket.io init`);
197
+
198
+ this.#io.engine.on("connection_error", (err) => {
199
+ this.LogEx(err.req); // the request object
200
+ this.LogEx(err.code); // the error code, for example 1
201
+ this.LogEx(err.message); // the error message, for example "Session ID unknown"
202
+ this.LogEx(err.context); // some additional error context
203
+ });
204
+ }
205
+
206
+ #GetTLSOptions = (): JSONObject => {
207
+ return {
208
+ key: fs.readFileSync(this.options.httpsServerKeyPath),
209
+ cert: fs.readFileSync(this.options.httpsServerCertificatePath)
210
+ };
211
+ }
212
+
213
+ #SetupExpressServer = async (useTls: boolean): Promise<void> => {
214
+ if (useTls) {
215
+ this.#httpServer = createServerHttps(this.#GetTLSOptions(), (this.#expressServer as STSExpressServer).App);
216
+ } else {
217
+ this.#httpServer = createServer((this.#expressServer as STSExpressServer).App);
218
+ }
219
+ if (this.options.prometheusSupport === true) {
220
+ this.#SetupPrometheusEndPoints((this.#expressServer as STSExpressServer).App);
221
+ }
222
+ if (this.options.wssServer === true) {
223
+ await this.#SetupWSSServer(true);
224
+ }
225
+ // https://stackoverflow.com/questions/21342828/node-express-unix-domain-socket-permissions
226
+ //@@httpServer.listen('/tmp/stsrest01.sock').on('listening', () =>
227
+ //@@httpServer.listen('/var/run/sts/stsrest01.sock').on('listening', () =>
228
+ this.#httpServer.listen(this.options.listenPort, () => {
229
+ //@@chmodSync(this.options.port, 511);
230
+ }).on('listening', () =>
231
+ {
232
+ this.LogEx(`live on ${this.options.endpoint}:${this.options.listenPort}${this.options.apiRoot}`);
233
+ });
234
+ }
235
+
236
+ #SetupTCPRawServer = async (): Promise<void> => {
237
+ // The second parameter is the automatic listener for the secureConnection event from the tls.Server class
238
+ this.#httpServer = tls.createServer(this.#GetTLSOptions(), this.#SetupTLSServer);
239
+ this.#httpServer.listen(this.options.listenPort, 'stscore.stsmda.org', () => {
240
+ console.log('TCP Server is running on port ' + this.options.listenPort + '.');
241
+ }).on('listening', () =>
242
+ {
243
+ this.LogEx(`TCP live on ${this.options.endpoint}:${this.options.listenPort}${this.options.apiRoot}`);
244
+ });
245
+ }
246
+
247
+ #SetupJSONRPCServer = async (): Promise<void> => {
248
+ const jaysonServer = new jayson.server();
249
+ // Supported methods here - move somewhere else ...
250
+ jaysonServer.method("add", function(args: any, callback: any) {
251
+ callback(null, args[0] + args[1]);
252
+ });
253
+ this.#httpServer = jaysonServer.tls(this.#GetTLSOptions());
254
+ (this.#httpServer as tls.Server).on('secureConnection', this.#SetupRPCServer);
255
+ this.#httpServer.listen(this.options.listenPort, 'stscore.stsmda.org', () => {
256
+ console.log('JSON RPC 2.0 Server is running on port ' + this.options.listenPort + '.');
257
+ }).on('listening', () =>
258
+ {
259
+ this.LogEx(`JSON RPC 2.0 live on ${this.options.endpoint}:${this.options.listenPort}${this.options.apiRoot}`);
260
+ });
261
+ }
262
+
263
+ ProcessTerminating = async (): Promise<void> => {
264
+ return;
265
+ }
266
+
267
+ // Terminate in order;
268
+ // forked worker threads (send signal)
269
+ // De-Register service
270
+ // systeminformation observers
271
+ // instrument timers (gauge etc.)
272
+ // publisher
273
+ // terminate UI (if loaded)
274
+ Terminate = async (clusterMode: boolean, clusterPerformExit: boolean, signal?: any): Promise<void> => {
275
+ if (this.#shuttingDown === false) {
276
+ this.#shuttingDown = true;
277
+
278
+ await this.ProcessTerminate();
279
+
280
+ await this.ProcessTerminating();
281
+
282
+ if (!clusterMode) {
283
+ if (this.GetUIController() !== null)
284
+ {
285
+ this.LogEx('Destroy the user interface controller.');
286
+ this.GetUIController().DestroyUI();
287
+ }
288
+
289
+ if (signal) {
290
+ this.LogEx(this.GetSignalColour(signal)(`Main Process (singleprocess): ${process.pid} received signal: ${signal}`));
291
+ } else {
292
+ this.LogEx(this.GetSignalColour(null)(`Main Process (singleprocess): ${process.pid} received Terminate without signal.`));
293
+ }
294
+ }
295
+
296
+ if (this.options.wssServer === true && this.#io !== null)
297
+ {
298
+ this.LogEx(`Disconnect Sockets.`);
299
+ if (this.socketIoHelper !== null) {
300
+ this.socketIoHelper.DisconnectSockets();
301
+ } else {
302
+ this.#io.disconnectSockets();
303
+ }
304
+ this.socketIoHelper = null;
305
+ this.#io = null;
306
+ // Note that this.#redisClient.disconnect() is not required becuase DisconnectSockets performs this action.
307
+ }
308
+
309
+ if (this.#httpServer) {
310
+ if (this.options.serverType === STSServerType.TCPRAW_TLS) {
311
+ this.#sockets.forEach((socket: net.Socket, index, array) => {
312
+ this.LogEx(`TCP Socket destroy, remote address: [${socket.remoteAddress}], remote port: [${socket.remotePort}]`.yellow);
313
+ socket.destroy();
314
+ //socket.end();
315
+ });
316
+ }
317
+ this.LogEx(`Closing httpServer.`);
318
+ await this.#httpServer.close();
319
+ }
320
+
321
+ if (this.options.useDatabase) {
322
+ this.LogEx(`Ending database connections and pools.`);
323
+ await this.TerminateDatabase();
324
+ //await this.accessLayer.enddatabase();
325
+ }
326
+
327
+ if (clusterMode) {
328
+ this.LogEx(`Performing exit value: [${clusterPerformExit}]`);
329
+ if (clusterPerformExit) {
330
+ this.LogEx(`Process will self terminate with process.exit(0).`);
331
+ } else {
332
+ this.LogEx(`Child process will not self terminate. Terminate will be handled by master process.`);
333
+ }
334
+ }
335
+
336
+ if (this.InstrumentController && this.InstrumentController.Workers.length > 0) {
337
+ this.LogEx(`Ending publisher.`);
338
+ setTimeout(() => {
339
+ if (this.InstrumentController && this.InstrumentController.Workers.length > 0) {
340
+ this.InstrumentController.InstrumentTerminate();
341
+ }
342
+ }, 100);
343
+ }
344
+
345
+ //@@ always return here appears to always cleanly exit
346
+ // and cleanly exit from socket.io cluster adaptor
347
+ // without return here, socket.io cluster adaptor terminates in an error state
348
+ // as the implementation relies on cluster.on to send messages to worker threads
349
+ // but these have already been closed from the process.exit(0) below.
350
+
351
+ await Sleep(1000); // Allow socket.io time to clean-up
352
+
353
+ if (clusterMode) {
354
+ if (clusterPerformExit) {
355
+ setTimeout(() => {
356
+ process.exit(0);
357
+ }, 0);
358
+ }
359
+ } else {
360
+ if (this.options.processExitOnTerminate && this.options.processExitOnTerminate === true) {
361
+ setTimeout(() => {
362
+ this.LogEx(`Performing process.exit(0).`);
363
+ process.exit(0);
364
+ }, this.#masterProcessExitTime); // Give the workers time to terminate gracefully
365
+ } else {
366
+ this.LogEx(`Performing process.exit(0) - Immediate.`);
367
+ }
368
+ }
369
+ } else {
370
+ this.LogEx(`Process already terminating.`);
371
+ }
372
+ }
373
+
374
+ SetupSTSServer = async(): Promise<void> => {
375
+ switch (this.options.serverType) {
376
+ case STSServerType.EXPRESS :
377
+ await this.#SetupExpressServer(false);
378
+ break;
379
+ case STSServerType.EXPRESS_TLS :
380
+ await this.#SetupExpressServer(true);
381
+ break;
382
+ case STSServerType.TCPRAW_TLS :
383
+ await this.#SetupTCPRawServer();
384
+ break;
385
+ case STSServerType.JSONRPC2_TLS :
386
+ await this.#SetupJSONRPCServer();
387
+ break;
388
+ }
389
+ }
390
+ }