jspurefix 5.8.4 → 5.9.1

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 (92) hide show
  1. package/.gitattributes +4 -0
  2. package/BACKPORT_PLAN.md +71 -0
  3. package/DEMO_PORT_PLAN.md +10 -0
  4. package/README.md +3 -0
  5. package/dist/config/js-fix-config.d.ts +4 -0
  6. package/dist/config/js-fix-config.js.map +1 -1
  7. package/dist/runtime/session-launcher.js +1 -1
  8. package/dist/runtime/session-launcher.js.map +1 -1
  9. package/dist/runtime/session-scope.d.ts +3 -0
  10. package/dist/runtime/session-scope.js +47 -0
  11. package/dist/runtime/session-scope.js.map +1 -0
  12. package/dist/sample/http/oms/app.js +3 -3
  13. package/dist/sample/http/oms/app.js.map +1 -1
  14. package/dist/sample/tcp/qf-md/app.js +3 -3
  15. package/dist/sample/tcp/qf-md/app.js.map +1 -1
  16. package/dist/sample/tcp/skeleton/app.js +1 -1
  17. package/dist/sample/tcp/skeleton/app.js.map +1 -1
  18. package/dist/sample/tcp/trade-capture/app-launcher.js +3 -3
  19. package/dist/sample/tcp/trade-capture/app-launcher.js.map +1 -1
  20. package/dist/transport/ascii/ascii-session.d.ts +17 -3
  21. package/dist/transport/ascii/ascii-session.js +121 -8
  22. package/dist/transport/ascii/ascii-session.js.map +1 -1
  23. package/dist/transport/duplex/fix-duplex.d.ts +1 -0
  24. package/dist/transport/duplex/fix-duplex.js +3 -0
  25. package/dist/transport/duplex/fix-duplex.js.map +1 -1
  26. package/dist/transport/duplex/tcp-duplex.d.ts +1 -0
  27. package/dist/transport/duplex/tcp-duplex.js +5 -0
  28. package/dist/transport/duplex/tcp-duplex.js.map +1 -1
  29. package/dist/transport/factory/msg-transport.d.ts +2 -0
  30. package/dist/transport/factory/msg-transport.js +17 -2
  31. package/dist/transport/factory/msg-transport.js.map +1 -1
  32. package/dist/transport/session/a-session-msg-factory.d.ts +1 -0
  33. package/dist/transport/session/a-session-msg-factory.js +4 -0
  34. package/dist/transport/session/a-session-msg-factory.js.map +1 -1
  35. package/dist/transport/session/clone-session-description.d.ts +6 -0
  36. package/dist/transport/session/clone-session-description.js +11 -0
  37. package/dist/transport/session/clone-session-description.js.map +1 -0
  38. package/dist/transport/session/fix-session.d.ts +5 -1
  39. package/dist/transport/session/fix-session.js +105 -28
  40. package/dist/transport/session/fix-session.js.map +1 -1
  41. package/dist/transport/session/index.d.ts +2 -0
  42. package/dist/transport/session/index.js +2 -0
  43. package/dist/transport/session/index.js.map +1 -1
  44. package/dist/transport/session/session-msg-factory.d.ts +1 -0
  45. package/dist/transport/session/session-msg-factory.js.map +1 -1
  46. package/dist/transport/session/session-registry.d.ts +23 -0
  47. package/dist/transport/session/session-registry.js +59 -0
  48. package/dist/transport/session/session-registry.js.map +1 -0
  49. package/dist/transport/tcp/tcp-acceptor-listener.d.ts +3 -0
  50. package/dist/transport/tcp/tcp-acceptor-listener.js +26 -4
  51. package/dist/transport/tcp/tcp-acceptor-listener.js.map +1 -1
  52. package/dist/transport/tcp/tcp-acceptor.d.ts +5 -0
  53. package/dist/transport/tcp/tcp-acceptor.js +74 -20
  54. package/dist/transport/tcp/tcp-acceptor.js.map +1 -1
  55. package/dist/transport/tcp/tcp-initiator.js +15 -5
  56. package/dist/transport/tcp/tcp-initiator.js.map +1 -1
  57. package/dist/transport/tcp/tcp-transport-description.d.ts +1 -0
  58. package/dist/transport/tcp/tcp-transport-description.js.map +1 -1
  59. package/dist/transport/tcp/tls-diagnostics.d.ts +5 -0
  60. package/dist/transport/tcp/tls-diagnostics.js +64 -0
  61. package/dist/transport/tcp/tls-diagnostics.js.map +1 -0
  62. package/dist/transport/tcp/tls-options-factory.d.ts +1 -0
  63. package/dist/transport/tcp/tls-options-factory.js +24 -2
  64. package/dist/transport/tcp/tls-options-factory.js.map +1 -1
  65. package/docs/acceptor.md +207 -0
  66. package/jsfix.test_client.txt +62 -75
  67. package/jsfix.test_server.txt +72 -72
  68. package/package.json +11 -10
  69. package/script/pack-local.js +68 -0
  70. package/src/config/js-fix-config.ts +8 -0
  71. package/src/runtime/session-launcher.ts +5 -1
  72. package/src/runtime/session-scope.ts +84 -0
  73. package/src/sample/http/oms/app.ts +3 -3
  74. package/src/sample/tcp/qf-md/app.ts +3 -3
  75. package/src/sample/tcp/skeleton/app.ts +1 -1
  76. package/src/sample/tcp/trade-capture/app-launcher.ts +3 -3
  77. package/src/transport/ascii/ascii-session.ts +186 -11
  78. package/src/transport/duplex/fix-duplex.ts +9 -0
  79. package/src/transport/duplex/tcp-duplex.ts +10 -0
  80. package/src/transport/factory/msg-transport.ts +20 -0
  81. package/src/transport/session/a-session-msg-factory.ts +18 -0
  82. package/src/transport/session/clone-session-description.ts +41 -0
  83. package/src/transport/session/fix-session.ts +153 -30
  84. package/src/transport/session/index.ts +2 -0
  85. package/src/transport/session/session-msg-factory.ts +13 -0
  86. package/src/transport/session/session-registry.ts +112 -0
  87. package/src/transport/tcp/tcp-acceptor-listener.ts +43 -5
  88. package/src/transport/tcp/tcp-acceptor.ts +92 -18
  89. package/src/transport/tcp/tcp-initiator.ts +26 -5
  90. package/src/transport/tcp/tcp-transport-description.ts +7 -0
  91. package/src/transport/tcp/tls-diagnostics.ts +76 -0
  92. package/src/transport/tcp/tls-options-factory.ts +34 -2
@@ -26,12 +26,16 @@ const tcp_acceptor_1 = require("./tcp-acceptor");
26
26
  const tsyringe_1 = require("tsyringe");
27
27
  const di_tokens_1 = require("../../runtime/di-tokens");
28
28
  const fix_entity_1 = require("../fix-entity");
29
+ const session_registry_1 = require("../session/session-registry");
29
30
  let TcpAcceptorListener = class TcpAcceptorListener extends fix_entity_1.FixEntity {
30
31
  constructor(config) {
32
+ var _a;
31
33
  super(config);
32
34
  this.config = config;
33
35
  this.acceptor = null;
34
36
  this.resolveStart = null;
37
+ this.sessionRegistry = (_a = config.sessionRegistry) !== null && _a !== void 0 ? _a : new session_registry_1.SessionRegistry(config.logFactory);
38
+ config.sessionRegistry = this.sessionRegistry;
35
39
  }
36
40
  start() {
37
41
  return __awaiter(this, void 0, void 0, function* () {
@@ -46,20 +50,38 @@ let TcpAcceptorListener = class TcpAcceptorListener extends fix_entity_1.FixEnti
46
50
  const acceptor = sessionContainer.resolve(tcp_acceptor_1.TcpAcceptor);
47
51
  this.acceptor = acceptor;
48
52
  acceptor.on('transport', (t) => {
49
- logger.info(`creates new transport using DI token ${di_tokens_1.DITokens.FixSession}.`);
50
- const acceptorSession = sessionContainer.resolve(di_tokens_1.DITokens.FixSession);
53
+ var _a, _b, _c, _d, _e;
54
+ const scope = (_b = (_a = t.config) === null || _a === void 0 ? void 0 : _a.sessionContainer) !== null && _b !== void 0 ? _b : sessionContainer;
55
+ const description = (_c = t.config) === null || _c === void 0 ? void 0 : _c.description;
56
+ logger.info(`transport ${t.id}: creating session via DI token ${di_tokens_1.DITokens.FixSession} ` +
57
+ `[${(_d = description === null || description === void 0 ? void 0 : description.SenderCompId) !== null && _d !== void 0 ? _d : '?'} -> ${(_e = description === null || description === void 0 ? void 0 : description.TargetCompID) !== null && _e !== void 0 ? _e : '?'}]`);
58
+ const acceptorSession = scope.resolve(di_tokens_1.DITokens.FixSession);
51
59
  this.emit('session', acceptorSession, t);
60
+ this.census(logger);
52
61
  acceptorSession.run(t).then(() => {
53
- logger.info('session ended normally, acceptor continues listening');
62
+ logger.info(`transport ${t.id}: session ended normally, acceptor continues listening`);
63
+ this.census(logger);
54
64
  }).catch((e) => {
55
- logger.warning(`session ended with error: ${e.message}, acceptor continues listening`);
65
+ logger.warning(`transport ${t.id}: session ended with error: ${e.message}, acceptor continues listening`);
66
+ this.census(logger);
56
67
  });
57
68
  });
69
+ acceptor.on('harvested', (tid, reason) => {
70
+ logger.info(`transport ${tid} harvested (${reason})`);
71
+ this.census(logger);
72
+ });
58
73
  this.resolveStart = resolve;
59
74
  acceptor.listen();
60
75
  }));
61
76
  });
62
77
  }
78
+ census(logger) {
79
+ var _a;
80
+ const acceptor = this.acceptor;
81
+ const transports = (_a = acceptor === null || acceptor === void 0 ? void 0 : acceptor.liveTransports()) !== null && _a !== void 0 ? _a : [];
82
+ logger.info(`acceptor census: transports=${transports.length} [${transports.join(', ')}] ` +
83
+ `sessions=${this.sessionRegistry.count} [${this.sessionRegistry.keys().join(', ')}]`);
84
+ }
63
85
  stop() {
64
86
  if (this.acceptor) {
65
87
  const logger = this.config.logFactory.logger('acceptor');
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-acceptor-listener.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-acceptor-listener.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAEA,iDAA4C;AAE5C,uCAA6C;AAE7C,uDAAkD;AAClD,8CAAyC;AAGlC,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,sBAAS;IAIhD,YAA4C,MAAoC;QAC9E,KAAK,CAAC,MAAM,CAAC,CAAA;QAD6C,WAAM,GAAN,MAAM,CAAc;QAHxE,aAAQ,GAAuB,IAAI,CAAA;QACnC,iBAAY,GAAkC,IAAI,CAAA;IAI1D,CAAC;IAEK,KAAK;;YACT,OAAO,MAAM,IAAI,OAAO,CAAM,CAAO,OAAO,EAAE,MAAM,EAAE,EAAE;gBACtD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;gBACxD,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAA;gBACrD,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,oBAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;oBACxD,MAAM,CAAC,IAAI,KAAK,CAAC,yCAAyC,oBAAQ,CAAC,UAAU,oBAAoB,CAAC,CAAC,CAAA;oBACnG,OAAM;gBACR,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;gBACxB,MAAM,QAAQ,GAAgB,gBAAgB,CAAC,OAAO,CAAc,0BAAW,CAAC,CAAA;gBAChF,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;gBAExB,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAe,EAAE,EAAE;oBAC3C,MAAM,CAAC,IAAI,CAAC,wCAAwC,oBAAQ,CAAC,UAAU,GAAG,CAAC,CAAA;oBAC3E,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAAa,oBAAQ,CAAC,UAAU,CAAC,CAAA;oBACjF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC,CAAA;oBAIxC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;wBAC/B,MAAM,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAA;oBACrE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAQ,EAAE,EAAE;wBACpB,MAAM,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC,OAAO,gCAAgC,CAAC,CAAA;oBACxF,CAAC,CAAC,CAAA;gBACJ,CAAC,CAAC,CAAA;gBAEF,IAAI,CAAC,YAAY,GAAG,OAAO,CAAA;gBAC3B,QAAQ,CAAC,MAAM,EAAE,CAAA;YACnB,CAAC,CAAA,CAAC,CAAA;QACJ,CAAC;KAAA;IAQD,IAAI;QACF,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;YACxD,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;YAChC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE;gBACvB,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAA;gBAC/B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;oBACtB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;oBACvB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;gBAC1B,CAAC;YACH,CAAC,CAAC,CAAA;YACF,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAA;QACtB,CAAC;IACH,CAAC;CACF,CAAA;AA3DY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,qBAAU,GAAE;IAKG,WAAA,IAAA,iBAAM,EAAC,oBAAQ,CAAC,YAAY,CAAC,CAAA;;GAJhC,mBAAmB,CA2D/B","sourcesContent":["import { IJsFixConfig } from '../../config'\nimport { FixAcceptor } from '../fix-acceptor'\nimport { TcpAcceptor } from './tcp-acceptor'\nimport { MsgTransport } from '../factory'\nimport { inject, injectable } from 'tsyringe'\nimport { FixSession } from '../session/fix-session'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { FixEntity } from '../fix-entity'\n\n@injectable()\nexport class TcpAcceptorListener extends FixEntity {\n private acceptor: FixAcceptor | null = null\n private resolveStart: ((value: any) => void) | null = null\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) {\n super(config)\n }\n\n async start (): Promise<any> {\n return await new Promise<any>(async (resolve, reject) => {\n const logger = this.config.logFactory.logger('acceptor')\n const sessionContainer = this.config.sessionContainer\n if (!sessionContainer.isRegistered(DITokens.FixSession)) {\n reject(new Error(`application must register a DI token '${DITokens.FixSession}' - see src/sample`))\n return\n }\n logger.info('starting.')\n const acceptor: FixAcceptor = sessionContainer.resolve<FixAcceptor>(TcpAcceptor)\n this.acceptor = acceptor\n\n acceptor.on('transport', (t: MsgTransport) => {\n logger.info(`creates new transport using DI token ${DITokens.FixSession}.`)\n const acceptorSession = sessionContainer.resolve<FixSession>(DITokens.FixSession)\n this.emit('session', acceptorSession, t)\n // Run the session but do NOT close the listener when it ends.\n // The acceptor keeps listening for new connections (reconnects).\n // This matches the C# TcpAcceptorListener accept-loop pattern.\n acceptorSession.run(t).then(() => {\n logger.info('session ended normally, acceptor continues listening')\n }).catch((e: Error) => {\n logger.warning(`session ended with error: ${e.message}, acceptor continues listening`)\n })\n })\n\n this.resolveStart = resolve\n acceptor.listen()\n })\n }\n\n /**\n * Explicitly stop the acceptor listener. Call this when the application\n * is shutting down (e.g., timeout, SIGINT). Without this call, the\n * acceptor keeps listening indefinitely — which is the correct\n * production behaviour for a FIX acceptor.\n */\n stop (): void {\n if (this.acceptor) {\n const logger = this.config.logFactory.logger('acceptor')\n logger.info('acceptor stopping')\n this.acceptor.close(() => {\n logger.info('acceptor closed.')\n if (this.resolveStart) {\n this.resolveStart(true)\n this.resolveStart = null\n }\n })\n this.acceptor = null\n }\n }\n}\n"]}
1
+ {"version":3,"file":"tcp-acceptor-listener.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-acceptor-listener.ts"],"names":[],"mappings":"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","sourcesContent":["import { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { FixAcceptor } from '../fix-acceptor'\nimport { TcpAcceptor } from './tcp-acceptor'\nimport { MsgTransport } from '../factory'\nimport { inject, injectable } from 'tsyringe'\nimport { FixSession } from '../session/fix-session'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { FixEntity } from '../fix-entity'\nimport { ISessionRegistry, SessionRegistry } from '../session/session-registry'\n\n@injectable()\nexport class TcpAcceptorListener extends FixEntity {\n private acceptor: FixAcceptor | null = null\n private resolveStart: ((value: any) => void) | null = null\n /**\n * One registry per listener, so a counterparty reconnecting onto a fresh socket\n * replaces its previous session instead of running alongside it (issue #153).\n * An application may supply its own via config.sessionRegistry.\n */\n public readonly sessionRegistry: ISessionRegistry\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) {\n super(config)\n this.sessionRegistry = config.sessionRegistry ?? new SessionRegistry(config.logFactory)\n config.sessionRegistry = this.sessionRegistry\n }\n\n async start (): Promise<any> {\n return await new Promise<any>(async (resolve, reject) => {\n const logger = this.config.logFactory.logger('acceptor')\n const sessionContainer = this.config.sessionContainer\n if (!sessionContainer.isRegistered(DITokens.FixSession)) {\n reject(new Error(`application must register a DI token '${DITokens.FixSession}' - see src/sample`))\n return\n }\n logger.info('starting.')\n const acceptor: FixAcceptor = sessionContainer.resolve<FixAcceptor>(TcpAcceptor)\n this.acceptor = acceptor\n\n acceptor.on('transport', (t: MsgTransport) => {\n // resolve from the transport's own scope, not the listener's container, so\n // this session gets the per-connection description, buffers and message\n // factory created by TcpAcceptor. Falls back to the listener container for\n // transports created outside the acceptor (tests, custom entities).\n const scope = t.config?.sessionContainer ?? sessionContainer\n const description = t.config?.description\n logger.info(`transport ${t.id}: creating session via DI token ${DITokens.FixSession} ` +\n `[${description?.SenderCompId ?? '?'} -> ${description?.TargetCompID ?? '?'}]`)\n const acceptorSession = scope.resolve<FixSession>(DITokens.FixSession)\n this.emit('session', acceptorSession, t)\n // a wildcard acceptor has not claimed its SessionId yet - report again once\n // the session ends, by which time the registry line is meaningful\n this.census(logger)\n // Run the session but do NOT close the listener when it ends.\n // The acceptor keeps listening for new connections (reconnects).\n // This matches the C# TcpAcceptorListener accept-loop pattern.\n acceptorSession.run(t).then(() => {\n logger.info(`transport ${t.id}: session ended normally, acceptor continues listening`)\n this.census(logger)\n }).catch((e: Error) => {\n logger.warning(`transport ${t.id}: session ended with error: ${e.message}, acceptor continues listening`)\n this.census(logger)\n })\n })\n\n acceptor.on('harvested', (tid: number, reason: string) => {\n logger.info(`transport ${tid} harvested (${reason})`)\n this.census(logger)\n })\n\n this.resolveStart = resolve\n acceptor.listen()\n })\n }\n\n /**\n * What is actually connected, and which SessionIds are claimed. Emitted whenever\n * the population changes so \"how many clients do I have, and who are they\" is\n * answerable from the server log alone.\n */\n private census (logger: IJsFixLogger): void {\n const acceptor = this.acceptor as TcpAcceptor | null\n const transports = acceptor?.liveTransports() ?? []\n logger.info(`acceptor census: transports=${transports.length} [${transports.join(', ')}] ` +\n `sessions=${this.sessionRegistry.count} [${this.sessionRegistry.keys().join(', ')}]`)\n }\n\n /**\n * Explicitly stop the acceptor listener. Call this when the application\n * is shutting down (e.g., timeout, SIGINT). Without this call, the\n * acceptor keeps listening indefinitely — which is the correct\n * production behaviour for a FIX acceptor.\n */\n stop (): void {\n if (this.acceptor) {\n const logger = this.config.logFactory.logger('acceptor')\n logger.info('acceptor stopping')\n this.acceptor.close(() => {\n logger.info('acceptor closed.')\n if (this.resolveStart) {\n this.resolveStart(true)\n this.resolveStart = null\n }\n })\n this.acceptor = null\n }\n }\n}\n"]}
@@ -12,8 +12,13 @@ export declare class TcpAcceptor extends FixAcceptor {
12
12
  unsecureServer(): void;
13
13
  tlsOptions(): TlsOptions | null;
14
14
  private onSocket;
15
+ private applyKeepAlive;
16
+ static defaultKeepAliveMs: number;
15
17
  listen(): void;
16
18
  close(callback?: (err?: Error) => void): void;
19
+ get transportCount(): number;
20
+ liveTransports(): string[];
21
+ private readonly accepted;
17
22
  private saveTransport;
18
23
  private harvestTransport;
19
24
  }
@@ -11,6 +11,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  return function (target, key) { decorator(target, key, paramIndex); }
13
13
  };
14
+ var TcpAcceptor_1;
14
15
  Object.defineProperty(exports, "__esModule", { value: true });
15
16
  exports.TcpAcceptor = void 0;
16
17
  const duplex_1 = require("../duplex");
@@ -19,14 +20,17 @@ const fix_acceptor_1 = require("../fix-acceptor");
19
20
  const net_1 = require("net");
20
21
  const tls_1 = require("tls");
21
22
  const tls_options_factory_1 = require("./tls-options-factory");
23
+ const tls_diagnostics_1 = require("./tls-diagnostics");
22
24
  const tsyringe_1 = require("tsyringe");
23
25
  const di_tokens_1 = require("../../runtime/di-tokens");
24
- let TcpAcceptor = class TcpAcceptor extends fix_acceptor_1.FixAcceptor {
26
+ const session_scope_1 = require("../../runtime/session-scope");
27
+ let TcpAcceptor = TcpAcceptor_1 = class TcpAcceptor extends fix_acceptor_1.FixAcceptor {
25
28
  constructor(config) {
26
29
  var _a, _b;
27
30
  super((_a = config.description.application) !== null && _a !== void 0 ? _a : null);
28
31
  this.config = config;
29
32
  this.nextId = 0;
33
+ this.accepted = {};
30
34
  this.logger = config.logFactory.logger(`${(_b = config.description.application) === null || _b === void 0 ? void 0 : _b.name}:TcpAcceptor`);
31
35
  const tlsOptions = this.tlsOptions();
32
36
  if (tlsOptions) {
@@ -52,21 +56,27 @@ let TcpAcceptor = class TcpAcceptor extends fix_acceptor_1.FixAcceptor {
52
56
  this.logger.info('create tls server');
53
57
  if (!tlsOptions)
54
58
  return;
59
+ this.logger.info(`tls server options ${(0, tls_diagnostics_1.describeServerOptions)(tlsOptions)}`);
55
60
  this.server = (0, tls_1.createServer)(tlsOptions, (tlsSocket) => {
56
61
  var _a;
57
62
  if ((_a = tcp === null || tcp === void 0 ? void 0 : tcp.tls) === null || _a === void 0 ? void 0 : _a.enableTrace) {
58
63
  this.logger.info('enabling tls session trace');
59
64
  tlsSocket.enableTrace();
60
65
  }
61
- if (tlsSocket.authorized) {
62
- tlsSocket.setEncoding('utf8');
63
- const id = this.getId();
64
- this.logger.info(`tls creates session ${id} ${tlsSocket.authorized}`);
65
- this.onSocket(id, tlsSocket, config);
66
+ this.logger.info(`tls negotiated ${(0, tls_diagnostics_1.describeNegotiated)(tlsSocket)}`);
67
+ this.logger.info(`tls peer certificate ${(0, tls_diagnostics_1.describePeerCertificate)(tlsSocket)}`);
68
+ if (!tlsSocket.authorized && tlsOptions.rejectUnauthorized !== false) {
69
+ this.logger.warning(`no transport created, unauthorized tls connection: ${String(tlsSocket.authorizationError)}`);
70
+ tlsSocket.end();
71
+ return;
66
72
  }
67
- else {
68
- this.logger.info('no transport created on tls with no authorized connection');
73
+ if (!tlsSocket.authorized) {
74
+ this.logger.warning(`accepting unauthorized tls peer (rejectUnauthorized: false): ${String(tlsSocket.authorizationError)}`);
69
75
  }
76
+ tlsSocket.setEncoding('utf8');
77
+ const id = this.getId();
78
+ this.logger.info(`tls creates session ${id} authorized ${tlsSocket.authorized}`);
79
+ this.onSocket(id, tlsSocket, config);
70
80
  });
71
81
  }
72
82
  catch (e) {
@@ -96,16 +106,35 @@ let TcpAcceptor = class TcpAcceptor extends fix_acceptor_1.FixAcceptor {
96
106
  return (tcp === null || tcp === void 0 ? void 0 : tcp.tls) ? tls_options_factory_1.TlsOptionsFactory.getTlsOptions(tcp === null || tcp === void 0 ? void 0 : tcp.tls) : null;
97
107
  }
98
108
  onSocket(id, socket, config) {
99
- const transport = new factory_1.MsgTransport(id, config, new duplex_1.TcpDuplex(socket));
100
- this.saveTransport(id, transport);
109
+ var _a, _b;
110
+ const sessionConfig = (0, session_scope_1.makeSessionScope)(config);
111
+ const remote = `${(_a = socket.remoteAddress) !== null && _a !== void 0 ? _a : '?'}:${(_b = socket.remotePort) !== null && _b !== void 0 ? _b : 0}`;
112
+ this.applyKeepAlive(id, socket);
113
+ this.logger.info(`transport ${id} from ${remote} given its own session scope`);
114
+ const transport = new factory_1.MsgTransport(id, sessionConfig, new duplex_1.TcpDuplex(socket));
115
+ this.saveTransport(id, transport, remote);
101
116
  transport.receiver.on('end', () => {
102
- this.harvestTransport(id);
117
+ this.harvestTransport(id, 'peer ended the connection');
103
118
  });
104
119
  transport.receiver.on('error', (e) => {
105
120
  this.logger.error(e);
106
- this.harvestTransport(id);
121
+ this.harvestTransport(id, `receiver error: ${e.message}`);
122
+ });
123
+ socket.on('close', (hadError) => {
124
+ this.harvestTransport(id, hadError ? 'socket closed after error' : 'socket closed');
107
125
  });
108
126
  }
127
+ applyKeepAlive(id, socket) {
128
+ var _a, _b;
129
+ const configured = (_b = (_a = this.config.description.application) === null || _a === void 0 ? void 0 : _a.tcp) === null || _b === void 0 ? void 0 : _b.keepAliveMs;
130
+ const keepAliveMs = configured !== null && configured !== void 0 ? configured : TcpAcceptor_1.defaultKeepAliveMs;
131
+ if (keepAliveMs <= 0) {
132
+ this.logger.info(`transport ${id} keep-alive disabled`);
133
+ return;
134
+ }
135
+ socket.setKeepAlive(true, keepAliveMs);
136
+ this.logger.info(`transport ${id} keep-alive enabled, initial delay ${keepAliveMs}ms`);
137
+ }
109
138
  listen() {
110
139
  var _a, _b;
111
140
  const port = (_b = (_a = this.config.description.application) === null || _a === void 0 ? void 0 : _a.tcp) === null || _b === void 0 ? void 0 : _b.port;
@@ -121,23 +150,48 @@ let TcpAcceptor = class TcpAcceptor extends fix_acceptor_1.FixAcceptor {
121
150
  close(callback) {
122
151
  var _a, _b, _c;
123
152
  const port = (_c = (_b = (_a = this.config.description.application) === null || _a === void 0 ? void 0 : _a.tcp) === null || _b === void 0 ? void 0 : _b.port) !== null && _c !== void 0 ? _c : -1;
124
- this.logger.info(`close listener on port ${port}`);
153
+ this.logger.info(`close listener on port ${port} with ${this.transportCount} live transport(s)`);
154
+ Object.keys(this.transports).forEach(id => {
155
+ var _a;
156
+ const tid = Number(id);
157
+ this.logger.info(`close: releasing transport ${tid}`);
158
+ (_a = this.transports[tid]) === null || _a === void 0 ? void 0 : _a.end();
159
+ });
125
160
  this.server.close(callback);
126
161
  }
127
- saveTransport(tid, transport) {
162
+ get transportCount() {
163
+ return Object.keys(this.transports).length;
164
+ }
165
+ liveTransports() {
166
+ return Object.keys(this.transports).map(id => {
167
+ var _a;
168
+ const meta = this.accepted[id];
169
+ const age = meta ? Math.round((Date.now() - meta.at) / 1000) : -1;
170
+ return `${id}@${(_a = meta === null || meta === void 0 ? void 0 : meta.remote) !== null && _a !== void 0 ? _a : '?'} up ${age}s`;
171
+ });
172
+ }
173
+ saveTransport(tid, transport, remote) {
128
174
  this.transports[tid] = transport;
129
- const keys = Object.keys(this.transports);
130
- this.logger.info(`new transport id = ${tid} created total transports = ${keys.length}`);
175
+ this.accepted[tid] = { remote, at: Date.now() };
176
+ this.logger.info(`new transport id = ${tid} from ${remote} created total transports = ${this.transportCount}`);
131
177
  this.emit('transport', transport);
132
178
  }
133
- harvestTransport(tid) {
179
+ harvestTransport(tid, reason) {
180
+ var _a;
181
+ if (!this.transports[tid])
182
+ return;
183
+ const meta = this.accepted[tid];
184
+ const lifetime = meta ? Math.round((Date.now() - meta.at) / 1000) : -1;
134
185
  delete this.transports[tid];
135
- const keys = Object.keys(this.transports);
136
- this.logger.info(`transport ${tid} ends total transports = ${keys.length}`);
186
+ delete this.accepted[tid];
187
+ this.logger.info(`transport ${tid} from ${(_a = meta === null || meta === void 0 ? void 0 : meta.remote) !== null && _a !== void 0 ? _a : '?'} harvested after ${lifetime}s ` +
188
+ `(${reason}) total transports = ${this.transportCount}`);
189
+ this.emit('harvested', tid, reason);
137
190
  }
138
191
  };
139
192
  exports.TcpAcceptor = TcpAcceptor;
140
- exports.TcpAcceptor = TcpAcceptor = __decorate([
193
+ TcpAcceptor.defaultKeepAliveMs = 30000;
194
+ exports.TcpAcceptor = TcpAcceptor = TcpAcceptor_1 = __decorate([
141
195
  (0, tsyringe_1.injectable)(),
142
196
  __param(0, (0, tsyringe_1.inject)(di_tokens_1.DITokens.IJsFixConfig)),
143
197
  __metadata("design:paramtypes", [Object])
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-acceptor.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-acceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import { TcpDuplex } from '../duplex'\nimport { MsgTransport } from '../factory'\nimport { FixAcceptor } from '../fix-acceptor'\nimport { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { createServer as netCreateServer, Server, Socket } from 'net'\nimport { createServer as tlsCreateServer, TlsOptions, TLSSocket } from 'tls'\nimport { TlsOptionsFactory } from './tls-options-factory'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\n\n@injectable()\nexport class TcpAcceptor extends FixAcceptor {\n private server: Server\n private readonly logger: IJsFixLogger\n private nextId: number = 0\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) {\n super(config.description.application ?? null)\n this.logger = config.logFactory.logger(`${config.description.application?.name}:TcpAcceptor`)\n const tlsOptions: TlsOptions | null = this.tlsOptions()\n if (tlsOptions) {\n this.tlsServer()\n } else {\n this.unsecureServer()\n }\n this.server.on('error', (err: Error) => {\n throw err\n })\n }\n\n getId (): number {\n this.nextId++\n return this.nextId\n }\n\n tlsServer (): void {\n try {\n const config: IJsFixConfig = this.config\n const tcp = this.config.description.application?.tcp\n const tlsOptions: TlsOptions | null = tcp?.tls ? TlsOptionsFactory.getTlsOptions(tcp.tls) : null\n this.logger.info('create tls server')\n if (!tlsOptions) return\n this.server = tlsCreateServer(tlsOptions, (tlsSocket: TLSSocket) => {\n if (tcp?.tls?.enableTrace) {\n this.logger.info('enabling tls session trace')\n tlsSocket.enableTrace()\n }\n if (tlsSocket.authorized) {\n tlsSocket.setEncoding('utf8')\n const id: number = this.getId()\n this.logger.info(`tls creates session ${id} ${tlsSocket.authorized}`)\n this.onSocket(id, tlsSocket, config)\n } else {\n this.logger.info('no transport created on tls with no authorized connection')\n }\n })\n } catch (e) {\n this.logger.error(e as Error)\n throw e\n }\n }\n\n unsecureServer (): void {\n try {\n const config = this.config\n this.logger.info('create unsecured server')\n this.server = netCreateServer((socket: Socket) => {\n const id: number = this.getId()\n this.logger.info(`net creates session ${id}`)\n socket.setNoDelay(true)\n this.onSocket(id, socket, config)\n })\n } catch (e) {\n this.logger.error(e as Error)\n throw e\n }\n }\n\n tlsOptions (): TlsOptions | null {\n const tcp = this.config.description.application?.tcp\n return tcp?.tls ? TlsOptionsFactory.getTlsOptions(tcp?.tls) : null\n }\n\n private onSocket (id: number, socket: Socket, config: IJsFixConfig): void {\n const transport: MsgTransport = new MsgTransport(id, config, new TcpDuplex(socket))\n this.saveTransport(id, transport)\n transport.receiver.on('end', () => {\n this.harvestTransport(id)\n })\n transport.receiver.on('error', (e: Error) => {\n this.logger.error(e)\n this.harvestTransport(id)\n })\n }\n\n public listen (): void {\n const port = this.config.description.application?.tcp?.port\n this.logger.info(`start to listen ${port}`)\n this.server.on('connection', () => {\n this.logger.info('insecure connection established')\n })\n this.server.on('secureConnection', (s) => {\n this.logger.info(`secure connection; client authorized: ${s.authorized}`)\n })\n this.server.listen(port)\n }\n\n public close (callback?: (err?: Error) => void): void {\n const port = this.config.description.application?.tcp?.port ?? -1\n this.logger.info(`close listener on port ${port}`)\n this.server.close(callback)\n }\n\n private saveTransport (tid: number, transport: MsgTransport): void {\n this.transports[tid] = transport\n const keys: string[] = Object.keys(this.transports)\n this.logger.info(`new transport id = ${tid} created total transports = ${keys.length}`)\n this.emit('transport', transport)\n }\n\n private harvestTransport (tid: number): void {\n delete this.transports[tid]\n const keys: string[] = Object.keys(this.transports)\n this.logger.info(`transport ${tid} ends total transports = ${keys.length}`)\n }\n}\n"]}
1
+ {"version":3,"file":"tcp-acceptor.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-acceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import { TcpDuplex } from '../duplex'\nimport { MsgTransport } from '../factory'\nimport { FixAcceptor } from '../fix-acceptor'\nimport { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { createServer as netCreateServer, Server, Socket } from 'net'\nimport { createServer as tlsCreateServer, TlsOptions, TLSSocket } from 'tls'\nimport { TlsOptionsFactory } from './tls-options-factory'\nimport { describeNegotiated, describePeerCertificate, describeServerOptions } from './tls-diagnostics'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { makeSessionScope } from '../../runtime/session-scope'\n\n@injectable()\nexport class TcpAcceptor extends FixAcceptor {\n private server: Server\n private readonly logger: IJsFixLogger\n private nextId: number = 0\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) {\n super(config.description.application ?? null)\n this.logger = config.logFactory.logger(`${config.description.application?.name}:TcpAcceptor`)\n const tlsOptions: TlsOptions | null = this.tlsOptions()\n if (tlsOptions) {\n this.tlsServer()\n } else {\n this.unsecureServer()\n }\n this.server.on('error', (err: Error) => {\n throw err\n })\n }\n\n getId (): number {\n this.nextId++\n return this.nextId\n }\n\n tlsServer (): void {\n try {\n const config: IJsFixConfig = this.config\n const tcp = this.config.description.application?.tcp\n const tlsOptions: TlsOptions | null = tcp?.tls ? TlsOptionsFactory.getTlsOptions(tcp.tls) : null\n this.logger.info('create tls server')\n if (!tlsOptions) return\n this.logger.info(`tls server options ${describeServerOptions(tlsOptions)}`)\n this.server = tlsCreateServer(tlsOptions, (tlsSocket: TLSSocket) => {\n if (tcp?.tls?.enableTrace) {\n this.logger.info('enabling tls session trace')\n tlsSocket.enableTrace()\n }\n this.logger.info(`tls negotiated ${describeNegotiated(tlsSocket)}`)\n this.logger.info(`tls peer certificate ${describePeerCertificate(tlsSocket)}`)\n // mirrors the initiator fix for #94 - an unauthorized peer is only fatal when the\n // caller has not explicitly opted out. the default stays secure: rejectUnauthorized\n // undefined means reject, exactly as before.\n if (!tlsSocket.authorized && tlsOptions.rejectUnauthorized !== false) {\n this.logger.warning(`no transport created, unauthorized tls connection: ${String(tlsSocket.authorizationError)}`)\n tlsSocket.end()\n return\n }\n if (!tlsSocket.authorized) {\n this.logger.warning(`accepting unauthorized tls peer (rejectUnauthorized: false): ${String(tlsSocket.authorizationError)}`)\n }\n tlsSocket.setEncoding('utf8')\n const id: number = this.getId()\n this.logger.info(`tls creates session ${id} authorized ${tlsSocket.authorized}`)\n this.onSocket(id, tlsSocket, config)\n })\n } catch (e) {\n this.logger.error(e as Error)\n throw e\n }\n }\n\n unsecureServer (): void {\n try {\n const config = this.config\n this.logger.info('create unsecured server')\n this.server = netCreateServer((socket: Socket) => {\n const id: number = this.getId()\n this.logger.info(`net creates session ${id}`)\n socket.setNoDelay(true)\n this.onSocket(id, socket, config)\n })\n } catch (e) {\n this.logger.error(e as Error)\n throw e\n }\n }\n\n tlsOptions (): TlsOptions | null {\n const tcp = this.config.description.application?.tcp\n return tcp?.tls ? TlsOptionsFactory.getTlsOptions(tcp?.tls) : null\n }\n\n private onSocket (id: number, socket: Socket, config: IJsFixConfig): void {\n // Every accepted connection gets its own DI scope: its own parse and transmit\n // buffers, its own session description (hence its own SessionId and message\n // store) and its own session message factory. Without this, concurrent clients\n // on one listener share buffers and collide on a single store.\n const sessionConfig = makeSessionScope(config)\n const remote = `${socket.remoteAddress ?? '?'}:${socket.remotePort ?? 0}`\n this.applyKeepAlive(id, socket)\n this.logger.info(`transport ${id} from ${remote} given its own session scope`)\n const transport: MsgTransport = new MsgTransport(id, sessionConfig, new TcpDuplex(socket))\n this.saveTransport(id, transport, remote)\n transport.receiver.on('end', () => {\n this.harvestTransport(id, 'peer ended the connection')\n })\n transport.receiver.on('error', (e: Error) => {\n this.logger.error(e)\n this.harvestTransport(id, `receiver error: ${e.message}`)\n })\n // 'end' only fires on a clean FIN. A socket dropped by a firewall, or one this\n // side destroyed after a failed graceful close, arrives here instead - without\n // it the transport would sit in the map forever.\n socket.on('close', (hadError: boolean) => {\n this.harvestTransport(id, hadError ? 'socket closed after error' : 'socket closed')\n })\n }\n\n /**\n * TCP keep-alive is the only mechanism that detects a peer which has vanished\n * without sending FIN or RST - the half open socket described in issue #153. The\n * session level TestRequest timeout is the protocol level backstop; this is the\n * transport level one.\n */\n private applyKeepAlive (id: number, socket: Socket): void {\n const configured = this.config.description.application?.tcp?.keepAliveMs\n const keepAliveMs = configured ?? TcpAcceptor.defaultKeepAliveMs\n if (keepAliveMs <= 0) {\n this.logger.info(`transport ${id} keep-alive disabled`)\n return\n }\n socket.setKeepAlive(true, keepAliveMs)\n this.logger.info(`transport ${id} keep-alive enabled, initial delay ${keepAliveMs}ms`)\n }\n\n public static defaultKeepAliveMs: number = 30000\n\n public listen (): void {\n const port = this.config.description.application?.tcp?.port\n this.logger.info(`start to listen ${port}`)\n this.server.on('connection', () => {\n this.logger.info('insecure connection established')\n })\n this.server.on('secureConnection', (s) => {\n this.logger.info(`secure connection; client authorized: ${s.authorized}`)\n })\n this.server.listen(port)\n }\n\n public close (callback?: (err?: Error) => void): void {\n const port = this.config.description.application?.tcp?.port ?? -1\n this.logger.info(`close listener on port ${port} with ${this.transportCount} live transport(s)`)\n // Server.close only stops accepting - it waits for existing connections to go.\n // Release them, otherwise a shutdown blocks on whichever counterparty happens to\n // be connected (and forever on one that is half open).\n Object.keys(this.transports).forEach(id => {\n const tid = Number(id)\n this.logger.info(`close: releasing transport ${tid}`)\n this.transports[tid]?.end()\n })\n this.server.close(callback)\n }\n\n /** how many connections this listener currently holds */\n public get transportCount (): number {\n return Object.keys(this.transports).length\n }\n\n /** ids of the live transports, for a periodic census in the application log */\n public liveTransports (): string[] {\n return Object.keys(this.transports).map(id => {\n const meta = this.accepted[id as any]\n const age = meta ? Math.round((Date.now() - meta.at) / 1000) : -1\n return `${id}@${meta?.remote ?? '?'} up ${age}s`\n })\n }\n\n private readonly accepted: Record<number, { remote: string, at: number }> = {}\n\n private saveTransport (tid: number, transport: MsgTransport, remote: string): void {\n this.transports[tid] = transport\n this.accepted[tid] = { remote, at: Date.now() }\n this.logger.info(`new transport id = ${tid} from ${remote} created total transports = ${this.transportCount}`)\n this.emit('transport', transport)\n }\n\n private harvestTransport (tid: number, reason: string): void {\n if (!this.transports[tid]) return\n const meta = this.accepted[tid]\n const lifetime = meta ? Math.round((Date.now() - meta.at) / 1000) : -1\n delete this.transports[tid]\n delete this.accepted[tid]\n this.logger.info(`transport ${tid} from ${meta?.remote ?? '?'} harvested after ${lifetime}s ` +\n `(${reason}) total transports = ${this.transportCount}`)\n this.emit('harvested', tid, reason)\n }\n}\n"]}
@@ -62,6 +62,7 @@ const util = __importStar(require("util"));
62
62
  const tls_1 = require("tls");
63
63
  const net_1 = require("net");
64
64
  const tls_options_factory_1 = require("./tls-options-factory");
65
+ const tls_diagnostics_1 = require("./tls-diagnostics");
65
66
  const tsyringe_1 = require("tsyringe");
66
67
  const di_tokens_1 = require("../../runtime/di-tokens");
67
68
  var InitiatorState;
@@ -113,8 +114,9 @@ let TcpInitiator = class TcpInitiator extends fix_initiator_1.FixInitiator {
113
114
  this.logger.info(`connecting with timeout ${timeoutSeconds}`);
114
115
  this.tryConnect()
115
116
  .then((t) => { resolve(t); })
116
- .catch((_) => {
117
- this.repeatConnect(timeoutSeconds)
117
+ .catch((first) => {
118
+ this.logger.info(`first connect attempt failed: ${first.message}`);
119
+ this.repeatConnect(timeoutSeconds, first)
118
120
  .then((t) => { resolve(t); })
119
121
  .catch((e) => { reject(e); });
120
122
  });
@@ -170,18 +172,24 @@ let TcpInitiator = class TcpInitiator extends fix_initiator_1.FixInitiator {
170
172
  const connectionOptions = tcp ? tls_options_factory_1.TlsOptionsFactory.getTlsConnectionOptions(tcp) : null;
171
173
  if (connectionOptions) {
172
174
  try {
175
+ this.logger.info(`tls connect with ${(0, tls_diagnostics_1.describeConnectionOptions)(connectionOptions)}`);
173
176
  tlsSocket = (0, tls_1.connect)(connectionOptions, () => {
174
177
  var _a;
175
178
  if (!tlsSocket)
176
179
  return null;
177
180
  this.logger.info(`client connected ${tlsSocket.authorized ? 'authorized' : 'unauthorized'}`);
178
- if (!tlsSocket.authorized) {
181
+ this.logger.info(`tls negotiated ${(0, tls_diagnostics_1.describeNegotiated)(tlsSocket)}`);
182
+ this.logger.info(`tls peer certificate ${(0, tls_diagnostics_1.describePeerCertificate)(tlsSocket)}`);
183
+ if (!tlsSocket.authorized && connectionOptions.rejectUnauthorized !== false) {
179
184
  const error = tlsSocket.authorizationError;
180
185
  this.logger.warning(`rejecting from state ${this.state} authorizationError ${error}`);
181
186
  tlsSocket.end();
182
187
  reject(error);
183
188
  }
184
189
  else {
190
+ if (!tlsSocket.authorized) {
191
+ this.logger.warning(`proceeding despite unauthorized TLS peer (rejectUnauthorized: false): ${tlsSocket.authorizationError}`);
192
+ }
185
193
  tlsSocket.setEncoding('utf8');
186
194
  const tlsDuplex = new duplex_1.TcpDuplex(tlsSocket);
187
195
  if ((_a = tcp === null || tcp === void 0 ? void 0 : tcp.tls) === null || _a === void 0 ? void 0 : _a.enableTrace) {
@@ -193,6 +201,7 @@ let TcpInitiator = class TcpInitiator extends fix_initiator_1.FixInitiator {
193
201
  }
194
202
  });
195
203
  tlsSocket.on('error', (err) => {
204
+ this.logger.warning(`tls socket error from state ${this.state}: ${err.message}`);
196
205
  reject(err);
197
206
  });
198
207
  }
@@ -225,7 +234,7 @@ let TcpInitiator = class TcpInitiator extends fix_initiator_1.FixInitiator {
225
234
  this.th = null;
226
235
  }
227
236
  }
228
- repeatConnect(timeoutSeconds) {
237
+ repeatConnect(timeoutSeconds, initialError) {
229
238
  return __awaiter(this, void 0, void 0, function* () {
230
239
  return yield new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
231
240
  var _a, _b;
@@ -234,7 +243,7 @@ let TcpInitiator = class TcpInitiator extends fix_initiator_1.FixInitiator {
234
243
  const timeoutPromise = promisify(setTimeout);
235
244
  const reconnectSeconds = (_a = application === null || application === void 0 ? void 0 : application.reconnectSeconds) !== null && _a !== void 0 ? _a : 5;
236
245
  let retries = 0;
237
- let lastError;
246
+ let lastError = initialError;
238
247
  const name = (_b = application === null || application === void 0 ? void 0 : application.name) !== null && _b !== void 0 ? _b : 'initiator';
239
248
  this.th = setInterval(() => {
240
249
  ++retries;
@@ -252,6 +261,7 @@ let TcpInitiator = class TcpInitiator extends fix_initiator_1.FixInitiator {
252
261
  this.clearTimer();
253
262
  this.state = InitiatorState.Stopped;
254
263
  const e = lastError !== null && lastError !== void 0 ? lastError : new Error(`${name}: timeout of ${timeoutSeconds} whilst connecting`);
264
+ this.logger.warning(`${name}: giving up after ${timeoutSeconds}s and ${retries} retries, last error: ${e.message}`);
255
265
  reject(e);
256
266
  }).catch(e => {
257
267
  reject(e);
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-initiator.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-initiator.ts"],"names":[],"mappings":"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","sourcesContent":["import { FixInitiator } from '../fix-initiator'\nimport { MsgTransport } from '../factory'\n\nimport { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { TcpDuplex, FixDuplex } from '../duplex'\n\nimport * as util from 'util'\nimport { connect as tlsConnect, ConnectionOptions, TLSSocket } from 'tls'\nimport { createConnection } from 'net'\nimport Timeout = NodeJS.Timeout\nimport { TlsOptionsFactory } from './tls-options-factory'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { ITcpTransportDescription } from './tcp-transport-description'\n\nexport enum InitiatorState {\n Idle = 1,\n Connecting = 2,\n Connected = 3,\n Stopped = 4\n}\n\n@injectable()\nexport class TcpInitiator extends FixInitiator {\n public tcp: ITcpTransportDescription | null\n public state: InitiatorState = InitiatorState.Idle\n private readonly logger: IJsFixLogger\n private duplex: FixDuplex\n private th: Timeout | null = null\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly jsFixConfig: IJsFixConfig) {\n super(jsFixConfig.description.application ?? null)\n const name = this.application?.name ?? 'initiator'\n this.logger = jsFixConfig.logFactory.logger(`${name}:TcpInitiator`)\n if (!this.application) {\n throw new Error('no application in session description.')\n }\n this.tcp = this.application.tcp ?? null\n if (!this.tcp) {\n throw new Error('no tcp in session description need tcp { host: hostname, port: port }')\n }\n }\n\n public end (): void {\n this.clearTimer()\n switch (this.state) {\n case InitiatorState.Connected: {\n this.logger.info('end')\n this.duplex.end()\n this.state = InitiatorState.Stopped\n break\n }\n\n default: {\n this.logger.info(`end :state ${this.state}`)\n this.state = InitiatorState.Stopped\n break\n }\n }\n }\n\n public async connect (timeoutSeconds: number): Promise<MsgTransport> {\n return await new Promise<MsgTransport>(async (resolve, reject) => {\n switch (this.state) {\n case InitiatorState.Idle: {\n this.state = InitiatorState.Connecting\n this.logger.info(`connecting with timeout ${timeoutSeconds}`)\n this.tryConnect()\n .then((t: MsgTransport) => { resolve(t) })\n .catch((_: Error) => {\n this.repeatConnect(timeoutSeconds)\n .then((t: MsgTransport) => { resolve(t) })\n .catch((e: Error) => { reject(e) })\n })\n break\n }\n\n default: {\n const e: Error = new Error('connect not valid from non idle state')\n this.logger.warning(`rejecting from state ${this.state}`)\n reject(e)\n }\n }\n })\n }\n\n private async unsecureDuplex (): Promise<TcpDuplex> {\n const tcp = this.tcp\n return await new Promise<TcpDuplex>((resolve, reject) => {\n try {\n this.logger.info('unsecureDuplex try to connect to endPoint')\n const socket = tcp\n ? createConnection(tcp, () => {\n try {\n this.logger.info('net.createConnection cb, resolving')\n if (socket) {\n const tcpDuplex = new TcpDuplex(socket)\n resolve(tcpDuplex)\n } else {\n reject(new Error('no socket in tcp initiator'))\n }\n } catch (e) {\n reject(e)\n }\n })\n : null\n socket?.on('error', (err) => {\n reject(err)\n })\n } catch (e) {\n reject(e)\n }\n })\n }\n\n private async tlsDuplex (): Promise<TcpDuplex > {\n return await new Promise<TcpDuplex>((resolve, reject) => {\n let tlsSocket: TLSSocket | null = null\n const tcp = this.tcp\n const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null\n if (connectionOptions) {\n try {\n tlsSocket = tlsConnect(connectionOptions, () => {\n if (!tlsSocket) return null\n this.logger.info(`client connected ${tlsSocket.authorized ? 'authorized' : 'unauthorized'}`)\n if (!tlsSocket.authorized) {\n const error = tlsSocket.authorizationError\n this.logger.warning(`rejecting from state ${this.state} authorizationError ${error}`)\n tlsSocket.end()\n reject(error)\n } else {\n tlsSocket.setEncoding('utf8')\n const tlsDuplex = new TcpDuplex(tlsSocket)\n if (tcp?.tls?.enableTrace) {\n this.logger.info('enabling tls session trace')\n tlsSocket.enableTrace()\n }\n this.logger.info('tlsDuplex resolving')\n resolve(tlsDuplex)\n }\n })\n tlsSocket.on('error', (err) => {\n reject(err)\n })\n } catch (e) {\n reject(e)\n }\n }\n })\n }\n\n private async tryConnect (): Promise < MsgTransport > {\n return await new Promise<MsgTransport>((resolve, reject) => {\n const tcp = this.tcp\n const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null\n const connector = connectionOptions ? this.tlsDuplex() : this.unsecureDuplex()\n this.logger.info(`tryConnect ${tcp?.host}:${tcp?.port}`)\n connector.then(duplex => {\n this.duplex = duplex\n resolve(new MsgTransport(0, this.jsFixConfig, duplex))\n }).catch(e => {\n reject(e)\n })\n })\n }\n\n public clearTimer (): void {\n if (this.th) {\n clearInterval(this.th)\n this.th = null\n }\n }\n\n private async repeatConnect (timeoutSeconds: number): Promise < MsgTransport > {\n return await new Promise<MsgTransport>(async (resolve, reject) => {\n const application = this.application\n const promisify = util.promisify\n const timeoutPromise = promisify(setTimeout)\n const reconnectSeconds = application?.reconnectSeconds ?? 5\n let retries = 0\n let lastError: Error\n const name = application?.name ?? 'initiator'\n this.th = setInterval(() => {\n ++retries\n this.tryConnect()\n .then((t: MsgTransport) => {\n this.state = InitiatorState.Connected\n this.clearTimer()\n resolve(t)\n }).catch((e: Error) => {\n lastError = e\n this.logger.info(`${name}: retries ${retries} ${e.message}`)\n })\n }, reconnectSeconds * 1000)\n timeoutPromise(timeoutSeconds * 1000).then(() => {\n this.clearTimer()\n this.state = InitiatorState.Stopped\n const e = lastError ?? new Error(`${name}: timeout of ${timeoutSeconds} whilst connecting`)\n reject(e)\n }).catch(e => {\n reject(e)\n })\n })\n }\n}\n"]}
1
+ {"version":3,"file":"tcp-initiator.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-initiator.ts"],"names":[],"mappings":"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","sourcesContent":["import { FixInitiator } from '../fix-initiator'\nimport { MsgTransport } from '../factory'\n\nimport { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { TcpDuplex, FixDuplex } from '../duplex'\n\nimport * as util from 'util'\nimport { connect as tlsConnect, ConnectionOptions, TLSSocket } from 'tls'\nimport { createConnection } from 'net'\nimport Timeout = NodeJS.Timeout\nimport { TlsOptionsFactory } from './tls-options-factory'\nimport { describeConnectionOptions, describeNegotiated, describePeerCertificate } from './tls-diagnostics'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { ITcpTransportDescription } from './tcp-transport-description'\n\nexport enum InitiatorState {\n Idle = 1,\n Connecting = 2,\n Connected = 3,\n Stopped = 4\n}\n\n@injectable()\nexport class TcpInitiator extends FixInitiator {\n public tcp: ITcpTransportDescription | null\n public state: InitiatorState = InitiatorState.Idle\n private readonly logger: IJsFixLogger\n private duplex: FixDuplex\n private th: Timeout | null = null\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly jsFixConfig: IJsFixConfig) {\n super(jsFixConfig.description.application ?? null)\n const name = this.application?.name ?? 'initiator'\n this.logger = jsFixConfig.logFactory.logger(`${name}:TcpInitiator`)\n if (!this.application) {\n throw new Error('no application in session description.')\n }\n this.tcp = this.application.tcp ?? null\n if (!this.tcp) {\n throw new Error('no tcp in session description need tcp { host: hostname, port: port }')\n }\n }\n\n public end (): void {\n this.clearTimer()\n switch (this.state) {\n case InitiatorState.Connected: {\n this.logger.info('end')\n this.duplex.end()\n this.state = InitiatorState.Stopped\n break\n }\n\n default: {\n this.logger.info(`end :state ${this.state}`)\n this.state = InitiatorState.Stopped\n break\n }\n }\n }\n\n public async connect (timeoutSeconds: number): Promise<MsgTransport> {\n return await new Promise<MsgTransport>(async (resolve, reject) => {\n switch (this.state) {\n case InitiatorState.Idle: {\n this.state = InitiatorState.Connecting\n this.logger.info(`connecting with timeout ${timeoutSeconds}`)\n this.tryConnect()\n .then((t: MsgTransport) => { resolve(t) })\n .catch((first: Error) => {\n // carry the first failure into repeatConnect. it used to be discarded, so a\n // connect() whose timeout expires before the first retry interval fires - the\n // default case, timeout and reconnectSeconds are both 5 - reported a generic\n // \"timeout whilst connecting\" and threw away the reason. see #94.\n this.logger.info(`first connect attempt failed: ${first.message}`)\n this.repeatConnect(timeoutSeconds, first)\n .then((t: MsgTransport) => { resolve(t) })\n .catch((e: Error) => { reject(e) })\n })\n break\n }\n\n default: {\n const e: Error = new Error('connect not valid from non idle state')\n this.logger.warning(`rejecting from state ${this.state}`)\n reject(e)\n }\n }\n })\n }\n\n private async unsecureDuplex (): Promise<TcpDuplex> {\n const tcp = this.tcp\n return await new Promise<TcpDuplex>((resolve, reject) => {\n try {\n this.logger.info('unsecureDuplex try to connect to endPoint')\n const socket = tcp\n ? createConnection(tcp, () => {\n try {\n this.logger.info('net.createConnection cb, resolving')\n if (socket) {\n const tcpDuplex = new TcpDuplex(socket)\n resolve(tcpDuplex)\n } else {\n reject(new Error('no socket in tcp initiator'))\n }\n } catch (e) {\n reject(e)\n }\n })\n : null\n socket?.on('error', (err) => {\n reject(err)\n })\n } catch (e) {\n reject(e)\n }\n })\n }\n\n private async tlsDuplex (): Promise<TcpDuplex > {\n return await new Promise<TcpDuplex>((resolve, reject) => {\n let tlsSocket: TLSSocket | null = null\n const tcp = this.tcp\n const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null\n if (connectionOptions) {\n try {\n this.logger.info(`tls connect with ${describeConnectionOptions(connectionOptions)}`)\n tlsSocket = tlsConnect(connectionOptions, () => {\n if (!tlsSocket) return null\n this.logger.info(`client connected ${tlsSocket.authorized ? 'authorized' : 'unauthorized'}`)\n this.logger.info(`tls negotiated ${describeNegotiated(tlsSocket)}`)\n this.logger.info(`tls peer certificate ${describePeerCertificate(tlsSocket)}`)\n // https://github.com/TimelordUK/jspurefix/issues/94 - honour rejectUnauthorized:\n // false rather than always rejecting on an unauthorized cert. Node's tls.connect()\n // never throws for an unauthorized peer by itself; it's the caller's job to decide\n // what to do with tlsSocket.authorized, and rejectUnauthorized is how a caller opts\n // out of that check - so only reject here when the caller didn't opt out.\n if (!tlsSocket.authorized && connectionOptions.rejectUnauthorized !== false) {\n const error = tlsSocket.authorizationError\n this.logger.warning(`rejecting from state ${this.state} authorizationError ${error}`)\n tlsSocket.end()\n reject(error)\n } else {\n if (!tlsSocket.authorized) {\n this.logger.warning(`proceeding despite unauthorized TLS peer (rejectUnauthorized: false): ${tlsSocket.authorizationError}`)\n }\n tlsSocket.setEncoding('utf8')\n const tlsDuplex = new TcpDuplex(tlsSocket)\n if (tcp?.tls?.enableTrace) {\n this.logger.info('enabling tls session trace')\n tlsSocket.enableTrace()\n }\n this.logger.info('tlsDuplex resolving')\n resolve(tlsDuplex)\n }\n })\n tlsSocket.on('error', (err) => {\n // previously silent - the socket error is the real reason a tls session failed,\n // and without it the caller only ever sees repeatConnect's generic timeout.\n this.logger.warning(`tls socket error from state ${this.state}: ${err.message}`)\n reject(err)\n })\n } catch (e) {\n reject(e)\n }\n }\n })\n }\n\n private async tryConnect (): Promise < MsgTransport > {\n return await new Promise<MsgTransport>((resolve, reject) => {\n const tcp = this.tcp\n const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null\n const connector = connectionOptions ? this.tlsDuplex() : this.unsecureDuplex()\n this.logger.info(`tryConnect ${tcp?.host}:${tcp?.port}`)\n connector.then(duplex => {\n this.duplex = duplex\n resolve(new MsgTransport(0, this.jsFixConfig, duplex))\n }).catch(e => {\n reject(e)\n })\n })\n }\n\n public clearTimer (): void {\n if (this.th) {\n clearInterval(this.th)\n this.th = null\n }\n }\n\n private async repeatConnect (timeoutSeconds: number, initialError?: Error): Promise < MsgTransport > {\n return await new Promise<MsgTransport>(async (resolve, reject) => {\n const application = this.application\n const promisify = util.promisify\n const timeoutPromise = promisify(setTimeout)\n const reconnectSeconds = application?.reconnectSeconds ?? 5\n let retries = 0\n let lastError: Error = initialError as Error\n const name = application?.name ?? 'initiator'\n this.th = setInterval(() => {\n ++retries\n this.tryConnect()\n .then((t: MsgTransport) => {\n this.state = InitiatorState.Connected\n this.clearTimer()\n resolve(t)\n }).catch((e: Error) => {\n lastError = e\n this.logger.info(`${name}: retries ${retries} ${e.message}`)\n })\n }, reconnectSeconds * 1000)\n timeoutPromise(timeoutSeconds * 1000).then(() => {\n this.clearTimer()\n this.state = InitiatorState.Stopped\n const e = lastError ?? new Error(`${name}: timeout of ${timeoutSeconds} whilst connecting`)\n this.logger.warning(`${name}: giving up after ${timeoutSeconds}s and ${retries} retries, last error: ${e.message}`)\n reject(e)\n }).catch(e => {\n reject(e)\n })\n })\n }\n}\n"]}
@@ -3,4 +3,5 @@ export interface ITcpTransportDescription {
3
3
  readonly port: number;
4
4
  readonly host: string;
5
5
  readonly tls?: ITlsOptions;
6
+ readonly keepAliveMs?: number;
6
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-transport-description.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-transport-description.ts"],"names":[],"mappings":"","sourcesContent":["import { ITlsOptions } from './tls-options'\n\nexport interface ITcpTransportDescription {\n readonly port: number\n readonly host: string\n readonly tls?: ITlsOptions\n}\n"]}
1
+ {"version":3,"file":"tcp-transport-description.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-transport-description.ts"],"names":[],"mappings":"","sourcesContent":["import { ITlsOptions } from './tls-options'\n\nexport interface ITcpTransportDescription {\n readonly port: number\n readonly host: string\n readonly tls?: ITlsOptions\n /**\n * TCP keep-alive probe delay in milliseconds for connections accepted by this\n * listener. Keep-alive is what lets the OS notice a peer that has vanished\n * without FIN or RST - the half open socket in issue #153. Set 0 to disable.\n * Default 30000.\n */\n readonly keepAliveMs?: number\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { ConnectionOptions, TLSSocket, TlsOptions } from 'tls';
2
+ export declare function describeConnectionOptions(options: ConnectionOptions): string;
3
+ export declare function describeServerOptions(options: TlsOptions): string;
4
+ export declare function describeNegotiated(socket: TLSSocket): string;
5
+ export declare function describePeerCertificate(socket: TLSSocket): string;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeConnectionOptions = describeConnectionOptions;
4
+ exports.describeServerOptions = describeServerOptions;
5
+ exports.describeNegotiated = describeNegotiated;
6
+ exports.describePeerCertificate = describePeerCertificate;
7
+ function describeCommonOptions(options) {
8
+ var _a, _b;
9
+ const parts = [];
10
+ parts.push(`key ${options.key ? 'supplied' : 'none'}`);
11
+ parts.push(`cert ${options.cert ? 'supplied' : 'none'}`);
12
+ const ca = options.ca;
13
+ const caCount = Array.isArray(ca) ? ca.length : (ca ? 1 : 0);
14
+ parts.push(`ca ${caCount}`);
15
+ parts.push(`rejectUnauthorized ${String((_a = options.rejectUnauthorized) !== null && _a !== void 0 ? _a : '(default true)')}`);
16
+ parts.push(`requestCert ${String((_b = options.requestCert) !== null && _b !== void 0 ? _b : '(default false)')}`);
17
+ if (options.minVersion)
18
+ parts.push(`minVersion ${options.minVersion}`);
19
+ if (options.maxVersion)
20
+ parts.push(`maxVersion ${options.maxVersion}`);
21
+ return parts;
22
+ }
23
+ function describeConnectionOptions(options) {
24
+ var _a, _b;
25
+ const parts = [`host ${(_a = options.host) !== null && _a !== void 0 ? _a : '?'}`, `port ${(_b = options.port) !== null && _b !== void 0 ? _b : '?'}`];
26
+ if (options.servername)
27
+ parts.push(`servername ${options.servername}`);
28
+ return parts.concat(describeCommonOptions(options)).join(', ');
29
+ }
30
+ function describeServerOptions(options) {
31
+ return describeCommonOptions(options).join(', ');
32
+ }
33
+ function describeNegotiated(socket) {
34
+ var _a;
35
+ const parts = [];
36
+ parts.push(`protocol ${(_a = socket.getProtocol()) !== null && _a !== void 0 ? _a : 'none'}`);
37
+ const cipher = socket.getCipher();
38
+ parts.push(`cipher ${cipher ? `${cipher.name} (${cipher.version})` : 'none'}`);
39
+ parts.push(`authorized ${socket.authorized}`);
40
+ if (!socket.authorized && socket.authorizationError) {
41
+ parts.push(`authorizationError ${String(socket.authorizationError)}`);
42
+ }
43
+ return parts.join(', ');
44
+ }
45
+ function describePeerCertificate(socket) {
46
+ var _a, _b, _c, _d;
47
+ const cert = socket.getPeerCertificate(false);
48
+ if (!cert || Object.keys(cert).length === 0) {
49
+ return 'peer presented no certificate';
50
+ }
51
+ const subject = cert.subject ? ((_a = cert.subject.CN) !== null && _a !== void 0 ? _a : JSON.stringify(cert.subject)) : '?';
52
+ const issuer = cert.issuer ? ((_b = cert.issuer.CN) !== null && _b !== void 0 ? _b : JSON.stringify(cert.issuer)) : '?';
53
+ const parts = [
54
+ `subject ${subject}`,
55
+ `issuer ${issuer}`,
56
+ `valid ${(_c = cert.valid_from) !== null && _c !== void 0 ? _c : '?'} to ${(_d = cert.valid_to) !== null && _d !== void 0 ? _d : '?'}`
57
+ ];
58
+ if (cert.subjectaltname)
59
+ parts.push(`san ${cert.subjectaltname}`);
60
+ if (cert.fingerprint256)
61
+ parts.push(`sha256 ${cert.fingerprint256}`);
62
+ return parts.join(', ');
63
+ }
64
+ //# sourceMappingURL=tls-diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tls-diagnostics.js","sourceRoot":"","sources":["../../../src/transport/tcp/tls-diagnostics.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConnectionOptions, TLSSocket, TlsOptions } from 'tls'\n\n/*\n tls failures are routinely reported as \"it just times out\" - see issues #94 and #151. the\n session either connects or it does not, and nothing in the log says which options were\n actually applied or what the peer presented. enableTrace exists but is node's raw handshake\n trace: all or nothing, written to stderr rather than through IJsFixLogger.\n\n these helpers render the small number of facts needed to diagnose a handshake, and are\n deliberately conservative about what they include - never key material, never a full\n certificate, only the identifying fields of one.\n */\n\nfunction describeCommonOptions (options: ConnectionOptions | TlsOptions): string[] {\n const parts: string[] = []\n // presence only - the values are private keys and certificate bodies.\n parts.push(`key ${options.key ? 'supplied' : 'none'}`)\n parts.push(`cert ${options.cert ? 'supplied' : 'none'}`)\n const ca = options.ca\n const caCount = Array.isArray(ca) ? ca.length : (ca ? 1 : 0)\n parts.push(`ca ${caCount}`)\n // the whole point of #94/#151 - make it unambiguous which value won.\n parts.push(`rejectUnauthorized ${String(options.rejectUnauthorized ?? '(default true)')}`)\n parts.push(`requestCert ${String(options.requestCert ?? '(default false)')}`)\n if (options.minVersion) parts.push(`minVersion ${options.minVersion}`)\n if (options.maxVersion) parts.push(`maxVersion ${options.maxVersion}`)\n return parts\n}\n\nexport function describeConnectionOptions (options: ConnectionOptions): string {\n const parts: string[] = [`host ${options.host ?? '?'}`, `port ${options.port ?? '?'}`]\n if (options.servername) parts.push(`servername ${options.servername}`)\n return parts.concat(describeCommonOptions(options)).join(', ')\n}\n\nexport function describeServerOptions (options: TlsOptions): string {\n return describeCommonOptions(options).join(', ')\n}\n\n/*\n what the handshake actually negotiated. the maintainer's suggestion in #94 was to reproduce\n the connection in powershell to see the negotiated suite - this puts the same facts in our\n own log instead.\n */\nexport function describeNegotiated (socket: TLSSocket): string {\n const parts: string[] = []\n parts.push(`protocol ${socket.getProtocol() ?? 'none'}`)\n const cipher = socket.getCipher()\n parts.push(`cipher ${cipher ? `${cipher.name} (${cipher.version})` : 'none'}`)\n parts.push(`authorized ${socket.authorized}`)\n if (!socket.authorized && socket.authorizationError) {\n parts.push(`authorizationError ${String(socket.authorizationError)}`)\n }\n return parts.join(', ')\n}\n\n/*\n identifying fields of the peer certificate only. getPeerCertificate() returns the full DER\n in .raw plus the public key - none of that belongs in a log, so pick fields explicitly.\n */\nexport function describePeerCertificate (socket: TLSSocket): string {\n const cert = socket.getPeerCertificate(false)\n if (!cert || Object.keys(cert).length === 0) {\n return 'peer presented no certificate'\n }\n const subject = cert.subject ? (cert.subject.CN ?? JSON.stringify(cert.subject)) : '?'\n const issuer = cert.issuer ? (cert.issuer.CN ?? JSON.stringify(cert.issuer)) : '?'\n const parts: string[] = [\n `subject ${subject}`,\n `issuer ${issuer}`,\n `valid ${cert.valid_from ?? '?'} to ${cert.valid_to ?? '?'}`\n ]\n if (cert.subjectaltname) parts.push(`san ${cert.subjectaltname}`)\n if (cert.fingerprint256) parts.push(`sha256 ${cert.fingerprint256}`)\n return parts.join(', ')\n}\n"]}
@@ -2,6 +2,7 @@ import { ConnectionOptions, TlsOptions } from 'tls';
2
2
  import { ITlsOptions } from './tls-options';
3
3
  import { ITcpTransportDescription } from './tcp-transport-description';
4
4
  export declare class TlsOptionsFactory {
5
+ static asBoolean(value: any): any;
5
6
  static read(filePath: string): string;
6
7
  static getTlsOptions(tls: ITlsOptions): TlsOptions | null;
7
8
  static getTlsConnectionOptions(tcp: ITcpTransportDescription): ConnectionOptions | null;