From c84947068ceadb878bf0c1f8823424e2f2043ca6 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Mon, 21 Jul 2025 18:44:59 +0000 Subject: [PATCH] BREAKING_CHANGE(core): remove legacy forwarding module in favor of route-based system - Removed the forwarding namespace export from main index - Removed TForwardingType and all forwarding handlers - Consolidated route helper functions into route-helpers.ts - All functionality is now available through the route-based system - Users must migrate from forwarding.* imports to direct route helper imports --- changelog.md | 9 + package.json | 2 +- test-output.log | 2749 ----------------- test/test.forwarding.ts | 3 - test/test.forwarding.unit.ts | 53 - test/test.route-utils.ts | 2 +- ts/forwarding/config/forwarding-types.ts | 76 - ts/forwarding/config/index.ts | 26 - ts/forwarding/factory/forwarding-factory.ts | 189 -- ts/forwarding/factory/index.ts | 5 - ts/forwarding/handlers/base-handler.ts | 155 - ts/forwarding/handlers/http-handler.ts | 163 - .../handlers/https-passthrough-handler.ts | 185 -- .../https-terminate-to-http-handler.ts | 312 -- .../https-terminate-to-https-handler.ts | 297 -- ts/forwarding/handlers/index.ts | 9 - ts/forwarding/index.ts | 35 - ts/index.ts | 1 - ts/proxies/smart-proxy/models/interfaces.ts | 1 - ts/proxies/smart-proxy/models/route-types.ts | 1 - ts/proxies/smart-proxy/utils/index.ts | 15 +- ts/proxies/smart-proxy/utils/route-helpers.ts | 281 +- .../smart-proxy/utils/route-patterns.ts | 403 --- 23 files changed, 264 insertions(+), 4708 deletions(-) delete mode 100644 test-output.log delete mode 100644 test/test.forwarding.unit.ts delete mode 100644 ts/forwarding/config/forwarding-types.ts delete mode 100644 ts/forwarding/config/index.ts delete mode 100644 ts/forwarding/factory/forwarding-factory.ts delete mode 100644 ts/forwarding/factory/index.ts delete mode 100644 ts/forwarding/handlers/base-handler.ts delete mode 100644 ts/forwarding/handlers/http-handler.ts delete mode 100644 ts/forwarding/handlers/https-passthrough-handler.ts delete mode 100644 ts/forwarding/handlers/https-terminate-to-http-handler.ts delete mode 100644 ts/forwarding/handlers/https-terminate-to-https-handler.ts delete mode 100644 ts/forwarding/handlers/index.ts delete mode 100644 ts/forwarding/index.ts delete mode 100644 ts/proxies/smart-proxy/utils/route-patterns.ts diff --git a/changelog.md b/changelog.md index 7fb3bcb..fb87b87 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # Changelog +## 2025-07-22 - 21.0.0 - BREAKING_CHANGE(forwarding) +Remove legacy forwarding module + +- Removed the `forwarding` namespace export from main index +- Removed TForwardingType and all forwarding handlers +- Consolidated route helper functions into route-helpers.ts +- All functionality is now available through the route-based system +- MIGRATION: Replace `import { forwarding } from '@push.rocks/smartproxy'` with direct imports of route helpers + ## 2025-07-21 - 20.0.2 - fix(docs) Update documentation to improve clarity diff --git a/package.json b/package.json index b88ec8c..fe65893 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@push.rocks/smartproxy", - "version": "20.0.2", + "version": "21.0.0", "private": false, "description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.", "main": "dist_ts/index.js", diff --git a/test-output.log b/test-output.log deleted file mode 100644 index bb7bf6d..0000000 --- a/test-output.log +++ /dev/null @@ -1,2749 +0,0 @@ - -> @push.rocks/smartproxy@19.6.17 test /mnt/data/lossless/push.rocks/smartproxy -> (tstest test/**/test*.ts --verbose --timeout 60 --logfile) - - -πŸ” Test Discovery - Mode: glob - Pattern: test/**/test*.ts - Found: 78 test file(s) - -▢️ test/core/routing/test.domain-matcher.ts (1/78) - Runtime: node.js - Test starting: DomainMatcher - exact match - βœ… DomainMatcher - exact match (0ms) - Test starting: DomainMatcher - case insensitive - βœ… DomainMatcher - case insensitive (0ms) - Test starting: DomainMatcher - wildcard matching - βœ… DomainMatcher - wildcard matching (0ms) - Test starting: DomainMatcher - FQDN normalization - βœ… DomainMatcher - FQDN normalization (0ms) - Test starting: DomainMatcher - edge cases - βœ… DomainMatcher - edge cases (0ms) - Test starting: DomainMatcher - specificity calculation - βœ… DomainMatcher - specificity calculation (0ms) - Test starting: DomainMatcher - findAllMatches - βœ… DomainMatcher - findAllMatches (0ms) - Summary: 7/7 PASSED in 1.3s - -▢️ test/core/routing/test.ip-matcher.ts (2/78) - Runtime: node.js - Test starting: IpMatcher - exact match - βœ… IpMatcher - exact match (0ms) - Test starting: IpMatcher - CIDR notation - βœ… IpMatcher - CIDR notation (0ms) - Test starting: IpMatcher - wildcard matching - βœ… IpMatcher - wildcard matching (0ms) - Test starting: IpMatcher - range matching - βœ… IpMatcher - range matching (0ms) - Test starting: IpMatcher - IPv6-mapped IPv4 - βœ… IpMatcher - IPv6-mapped IPv4 (0ms) - Test starting: IpMatcher - IP validation - βœ… IpMatcher - IP validation (0ms) - Test starting: IpMatcher - isAuthorized - βœ… IpMatcher - isAuthorized (0ms) - Test starting: IpMatcher - specificity calculation - βœ… IpMatcher - specificity calculation (1ms) - Test starting: IpMatcher - edge cases - βœ… IpMatcher - edge cases (0ms) - Summary: 9/9 PASSED in 1.3s - -▢️ test/core/routing/test.path-matcher.ts (3/78) - Runtime: node.js - Test starting: PathMatcher - exact match - βœ… PathMatcher - exact match (0ms) - Test starting: PathMatcher - no match - βœ… PathMatcher - no match (0ms) - Test starting: PathMatcher - parameter extraction - βœ… PathMatcher - parameter extraction (0ms) - Test starting: PathMatcher - multiple parameters - βœ… PathMatcher - multiple parameters (1ms) - Test starting: PathMatcher - wildcard matching - Error details: - Expected value to equal "users/123/profile" -  - Diff: -  Line 1: - users/123/profile -  Line 1: + /users/123/profile - ❌ PathMatcher - wildcard matching (0ms) - ⟦TSTEST:META:{"time":4,"retry":0,"error":{"message":"Expected value to equal \"users/123/profile\"","stack":"Error: Expected value to equal \"users/123/profile\"\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Assertion.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Assertion.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at Assertion.toEqual (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:332:17)\n at Proxy. (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.expect.wrapper.ts:33:38)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/core/routing/test.path-matcher.ts:35:32)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 5, - "error": { - "message": "Expected value to equal \"users/123/profile\"", - "stack": "Error: Expected value to equal \"users/123/profile\"\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Assertion.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Assertion.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at Assertion.toEqual (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:332:17)\n at Proxy. (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.expect.wrapper.ts:33:38)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/core/routing/test.path-matcher.ts:35:32)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)" - } - } - Error details: - Error: Expected value to equal "users/123/profile" - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Assertion.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Assertion.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at Assertion.toEqual (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:332:17) - at Proxy. (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.expect.wrapper.ts:33:38) - at (/mnt/data/lossless/push.rocks/smartproxy/test/core/routing/test.path-matcher.ts:35:32) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - Error: Expected value to equal "users/123/profile" - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Assertion.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Assertion.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at Assertion.toEqual (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:332:17) - at Proxy. (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.expect.wrapper.ts:33:38) - at (/mnt/data/lossless/push.rocks/smartproxy/test/core/routing/test.path-matcher.ts:35:32) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - Test starting: PathMatcher - mixed parameters and wildcards - Error details: - Expected value to equal "users/123" -  - Diff: -  Line 1: - users/123 -  Line 1: + /users/123 - ❌ PathMatcher - mixed parameters and wildcards (0ms) - ⟦TSTEST:META:{"time":0,"retry":0,"error":{"message":"Expected value to equal \"users/123\"","stack":"Error: Expected value to equal \"users/123\"\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Assertion.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Assertion.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at Assertion.toEqual (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:332:17)\n at Proxy. (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.expect.wrapper.ts:33:38)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/core/routing/test.path-matcher.ts:42:32)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 6, - "error": { - "message": "Expected value to equal \"users/123\"", - "stack": "Error: Expected value to equal \"users/123\"\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Assertion.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Assertion.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at Assertion.toEqual (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:332:17)\n at Proxy. (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.expect.wrapper.ts:33:38)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/core/routing/test.path-matcher.ts:42:32)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)" - } - } - Error details: - Error: Expected value to equal "users/123" - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Assertion.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Assertion.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at Assertion.toEqual (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:332:17) - at Proxy. (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.expect.wrapper.ts:33:38) - at (/mnt/data/lossless/push.rocks/smartproxy/test/core/routing/test.path-matcher.ts:42:32) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - Error: Expected value to equal "users/123" - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Assertion.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Assertion.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at Assertion.toEqual (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:332:17) - at Proxy. (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.expect.wrapper.ts:33:38) - at (/mnt/data/lossless/push.rocks/smartproxy/test/core/routing/test.path-matcher.ts:42:32) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - Test starting: PathMatcher - trailing slash normalization - βœ… PathMatcher - trailing slash normalization (0ms) - Test starting: PathMatcher - root path handling - βœ… PathMatcher - root path handling (0ms) - Test starting: PathMatcher - specificity calculation - βœ… PathMatcher - specificity calculation (1ms) - Test starting: PathMatcher - findAllMatches - βœ… PathMatcher - findAllMatches (1ms) - Test starting: PathMatcher - edge cases - βœ… PathMatcher - edge cases (0ms) - Test 5 failed with status error: - || PathMatcher - wildcard matching - || for more information please take a look the logs above - Test 6 failed with status error: - || PathMatcher - mixed parameters and wildcards - || for more information please take a look the logs above - Summary: 9 passed, 2 failed of 11 tests in 1.3s - -▢️ test/core/utils/test.async-utils.ts (4/78) - Runtime: node.js - Test starting: delay should pause execution for specified milliseconds - βœ… delay should pause execution for specified milliseconds (102ms) - Test starting: retryWithBackoff should retry failed operations - βœ… retryWithBackoff should retry failed operations (31ms) - Test starting: retryWithBackoff should throw after max attempts - βœ… retryWithBackoff should throw after max attempts (11ms) - Test starting: withTimeout should complete operations within timeout - βœ… withTimeout should complete operations within timeout (50ms) - Test starting: withTimeout should throw on timeout - βœ… withTimeout should throw on timeout (50ms) - Test starting: parallelLimit should respect concurrency limit - βœ… parallelLimit should respect concurrency limit (151ms) - Test starting: debounceAsync should debounce function calls - βœ… debounceAsync should debounce function calls (151ms) - Test starting: AsyncMutex should ensure exclusive access - βœ… AsyncMutex should ensure exclusive access (32ms) - Test starting: CircuitBreaker should open after failures - βœ… CircuitBreaker should open after failures (151ms) - Summary: 9/9 PASSED in 2.0s - -▢️ test/core/utils/test.binary-heap.ts (5/78) - Runtime: node.js - Test starting: should create empty heap - βœ… should create empty heap (1ms) - Test starting: should insert and extract in correct order - βœ… should insert and extract in correct order (1ms) - Test starting: should work with custom objects and comparator - βœ… should work with custom objects and comparator (0ms) - Test starting: should support reverse order (max heap) - βœ… should support reverse order (max heap) (0ms) - Test starting: should extract by predicate - βœ… should extract by predicate (0ms) - Test starting: should extract by key - βœ… should extract by key (0ms) - Test starting: should throw when using key operations without extractKey - βœ… should throw when using key operations without extractKey (0ms) - Test starting: should handle duplicates correctly - βœ… should handle duplicates correctly (0ms) - Test starting: should convert to array without modifying heap - βœ… should convert to array without modifying heap (0ms) - Test starting: should clear the heap - βœ… should clear the heap (0ms) - Test starting: should handle complex extraction patterns - βœ… should handle complex extraction patterns (0ms) - Summary: 11/11 PASSED in 1.2s - -▢️ test/core/utils/test.fs-utils.ts (6/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< - Test starting: should create and check directory existence - βœ… should create and check directory existence (1ms) - Test starting: should write and read text files - βœ… should write and read text files (2ms) - Test starting: should write and read JSON files - βœ… should write and read JSON files (1ms) - Test starting: should copy files - βœ… should copy files (1ms) - Test starting: should move files - βœ… should move files (1ms) - Test starting: should list files in directory - βœ… should list files in directory (0ms) - Test starting: should list files with full paths - βœ… should list files with full paths (1ms) - Test starting: should get file stats - βœ… should get file stats (0ms) - Test starting: should handle non-existent files gracefully - βœ… should handle non-existent files gracefully (0ms) - Test starting: should remove files - βœ… should remove files (1ms) - Test starting: should ensure file exists - βœ… should ensure file exists (0ms) - Test starting: should recursively list files - βœ… should recursively list files (1ms) - Test starting: should clean up test directory - βœ… should clean up test directory (1ms) - Summary: 13/13 PASSED in 2.2s - -▢️ test/core/utils/test.ip-utils.ts (7/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< - Test starting: ip-utils - normalizeIP - βœ… ip-utils - normalizeIP (1ms) - Test starting: ip-utils - isGlobIPMatch - βœ… ip-utils - isGlobIPMatch (3ms) - Test starting: ip-utils - isIPAuthorized - βœ… ip-utils - isIPAuthorized (1ms) - Test starting: ip-utils - isPrivateIP - βœ… ip-utils - isPrivateIP (0ms) - Test starting: ip-utils - isPublicIP - βœ… ip-utils - isPublicIP (0ms) - Test starting: ip-utils - cidrToGlobPatterns - βœ… ip-utils - cidrToGlobPatterns (0ms) - Summary: 6/6 PASSED in 2.3s - -▢️ test/core/utils/test.lifecycle-component.ts (8/78) - Runtime: node.js - Test starting: should manage timers properly - βœ… should manage timers properly (202ms) - Test starting: should manage event listeners properly - βœ… should manage event listeners properly (0ms) - Test starting: should prevent timer execution after cleanup - βœ… should prevent timer execution after cleanup (150ms) - Test starting: should handle child components - βœ… should handle child components (100ms) - Test starting: should handle multiple cleanup calls gracefully - βœ… should handle multiple cleanup calls gracefully (0ms) - Test starting: should clear specific timers - βœ… should clear specific timers (151ms) - Test starting: should clear specific intervals - βœ… should clear specific intervals (220ms) - Test starting: should handle once event listeners - βœ… should handle once event listeners (1ms) - Test starting: should not create timers when shutting down - βœ… should not create timers when shutting down (50ms) - Summary: 9/9 PASSED in 2.1s - -▢️ test/core/utils/test.shared-security-manager.ts (9/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< - Test starting: Shared Security Manager - βœ… Shared Security Manager (1ms) - Summary: 1/1 PASSED in 2.2s - -▢️ test/core/utils/test.validation-utils.ts (10/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< - Test starting: validation-utils - isValidPort - βœ… validation-utils - isValidPort (0ms) - Test starting: validation-utils - isValidDomainName - βœ… validation-utils - isValidDomainName (0ms) - Test starting: validation-utils - isValidEmail - βœ… validation-utils - isValidEmail (0ms) - Test starting: validation-utils - isValidCertificate - βœ… validation-utils - isValidCertificate (1ms) - Test starting: validation-utils - isValidPrivateKey - βœ… validation-utils - isValidPrivateKey (0ms) - Test starting: validation-utils - validateDomainOptions - βœ… validation-utils - validateDomainOptions (0ms) - Test starting: validation-utils - validateAcmeOptions - βœ… validation-utils - validateAcmeOptions (0ms) - Summary: 7/7 PASSED in 2.3s - -▢️ test/test.acme-http-challenge.ts (11/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should handle HTTP requests on port 80 for ACME challenges -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 18080 is used by 1 routes: acme-test-route -   info:  SmartProxy starting with 1 ports: 18080 -   info:  SmartProxy -> OK: Now listening on port 18080 -   info:  No routes require certificate management -  debug  MetricsCollector started -   info:  New connection from ::ffff:127.0.0.1 on port 18080. Active connections: 1 -   info:  Handling socket-handler action for route acme-test-route -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 18080 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 18080 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should handle HTTP requests on port 80 for ACME challenges (27ms) - Test starting: should parse HTTP headers correctly -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 18081 is used by 1 routes: header-test-route -   info:  SmartProxy starting with 1 ports: 18081 -   info:  SmartProxy -> OK: Now listening on port 18081 -   info:  No routes require certificate management -  debug  MetricsCollector started -  debug  Connection 1zmgol0smz7892s0rtaqq closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 18081. Active connections: 1 -   info:  Handling socket-handler action for route header-test-route -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 18081 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 18081 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s -   info:  SmartProxy shutdown complete. - βœ… should parse HTTP headers correctly (5ms) -  debug  Connection 7d9dtenko8n68ci30cnx8u closed during immediate routing: immediate-route-client_closed -   info:  [SUMMARY] 1 HttpProxy connections terminated in 1s - Summary: 2/2 PASSED in 3.3s - -▢️ test/test.acme-http01-challenge.ts (12/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should correctly handle HTTP-01 challenge requests with initial data chunk -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8080 is used by 1 routes: acme-challenge-route -   info:  SmartProxy starting with 1 ports: 8080 -   info:  SmartProxy -> OK: Now listening on port 8080 -   info:  No routes require certificate management -  debug  MetricsCollector started -   info:  New connection from ::ffff:127.0.0.1 on port 8080. Active connections: 1 -   info:  Handling socket-handler action for route acme-challenge-route - Received request: GET /.well-known/acme-challenge/test-acme-http01-challenge-token -  debug  Connection ctk31t255pd9c33wgs7iol closed during immediate routing: immediate-route-client_closed -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8080 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8080 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s -   info:  SmartProxy shutdown complete. - βœ… should correctly handle HTTP-01 challenge requests with initial data chunk (113ms) - Test starting: should return 404 for non-existent challenge tokens -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8081 is used by 1 routes: acme-challenge-route -   info:  SmartProxy starting with 1 ports: 8081 -   info:  SmartProxy -> OK: Now listening on port 8081 -   info:  No routes require certificate management -  debug  MetricsCollector started -   info:  New connection from ::ffff:127.0.0.1 on port 8081. Active connections: 1 -   info:  Handling socket-handler action for route acme-challenge-route -  debug  Connection b85l4hhzztejnaxzf4lee9 closed during immediate routing: immediate-route-client_closed -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8081 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8081 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s -   info:  SmartProxy shutdown complete. - βœ… should return 404 for non-existent challenge tokens (103ms) - Summary: 2/2 PASSED in 3.4s - -▢️ test/test.acme-route-creation.ts (13/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should create ACME challenge route -   info:  Route manager configured with 2 routes across 2 ports -   info:  Updated RouteManager with 2 routes -  debug  Port 18443 is used by 1 routes: secure-route -  debug  Port 18080 is used by 1 routes: acme-challenge -   info:  SmartProxy starting with 2 ports: 18080, 18443 -   info:  SmartProxy -> OK: Now listening on port 18080 -   info:  SmartProxy -> OK: Now listening on port 18443 -   info:  No routes require certificate management -  debug  MetricsCollector started -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 18080 reference count decreased to 0 -  debug  Port 18443 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 18080 -   info:  SmartProxy -> Stopped listening on port 18443 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should create ACME challenge route (6ms) - Test starting: should handle HTTP request parsing correctly -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 18090 is used by 1 routes: test-static -   info:  SmartProxy starting with 1 ports: 18090 -   info:  SmartProxy -> OK: Now listening on port 18090 -   info:  No routes require certificate management -  debug  MetricsCollector started -   info:  New connection from ::ffff:127.0.0.1 on port 18090. Active connections: 1 -   info:  Handling socket-handler action for route test-static -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 18090 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 18090 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should handle HTTP request parsing correctly (12ms) -  debug  Connection uczlsw6loqkmw2nt7506mi closed during immediate routing: immediate-route-client_closed -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s - Summary: 2/2 PASSED in 2.3s - -▢️ test/test.acme-simple.ts (14/78) - Runtime: node.js - Test starting: should parse HTTP requests correctly - Test server listening on port 18091 - βœ… should parse HTTP requests correctly (8ms) - Test starting: should configure ACME challenge route - βœ… should configure ACME challenge route (1ms) - Summary: 2/2 PASSED in 1.2s - -▢️ test/test.acme-state-manager.node.ts (15/78) - Runtime: node.js - Test starting: AcmeStateManager should track challenge routes correctly - βœ… AcmeStateManager should track challenge routes correctly (0ms) - Test starting: AcmeStateManager should track port allocations - βœ… AcmeStateManager should track port allocations (0ms) - Test starting: AcmeStateManager should select primary route by priority - βœ… AcmeStateManager should select primary route by priority (0ms) - Test starting: AcmeStateManager should handle clear operation - βœ… AcmeStateManager should handle clear operation (1ms) - Summary: 4/4 PASSED in 1.2s - -▢️ test/test.acme-timing-simple.ts (16/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should defer certificate provisioning until ports are ready -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   WARN ->  1 configuration warnings found -   WARN ->  Port 80 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 80 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 8443 is used by 1 routes: test-route -   info:  SmartProxy starting with 1 ports: 8443 -   info:  SmartProxy -> OK: Now listening on port 8443 - Ports are now listening -   info:  Using route-level ACME configuration from route 'test-route' with email: test@local.dev - Creating mock cert manager - Mock cert manager initialized -   info:  Starting certificate provisioning now that ports are ready - Mock certificate provisioning (ports are ready) -  debug  MetricsCollector started - Operation order: [ - 'ports-starting', - 'ports-ready', - 'create-cert-manager', - 'cert-manager-init', - 'cert-provisioning' - ] -   info:  SmartProxy shutting down... -   info:  Certificate manager stopped -   info:  NFTablesManager stopped -  debug  Port 8443 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8443 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should defer certificate provisioning until ports are ready (6ms) - Summary: 1/1 PASSED in 2.3s - -▢️ test/test.acme-timing.ts (17/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should defer certificate provisioning until after ports are listening -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   WARN ->  1 configuration warnings found -   WARN ->  Port 8080 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 8080 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 8443 is used by 1 routes: test-acme-route -   info:  SmartProxy starting with 1 ports: 8443 - [INFO] Route manager configured with 0 routes across 0 ports  - [INFO] Updated RouteManager with 0 routes  - [WARN] CertificateManager is deprecated - use SmartCertManager instead  - [INFO] Loaded default certificates from filesystem (sync - deprecated)  - Initialized HttpProxy on port 8845 - [INFO] Updating route configurations (0 routes)  - [INFO] Route manager configured with 0 routes across 0 ports  - [INFO] Updated RouteManager with 0 routes  - HttpRouter initialized with 0 routes (0 unique hosts) - [INFO] HttpRouter initialized with 0 routes (0 unique hosts)  - HttpRouter initialized with 0 routes (0 unique hosts) - [INFO] HttpRouter initialized with 0 routes (0 unique hosts)  - [INFO] Route configuration updated with 0 routes  - [WARN] updateRoutes is deprecated - use SmartCertManager instead  - [INFO] WebSocket handler initialized  - [INFO] HttpProxy started on port 8845  -   info:  SmartProxy -> OK: Now listening on port 8443 -   info:  Using top-level ACME configuration with email: test@test.local -   info:  Starting certificate provisioning now that ports are ready -  debug  MetricsCollector started -   info:  SmartProxy shutting down... -   info:  Certificate manager stopped -   info:  NFTablesManager stopped -  debug  Port 8443 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8443 -   info:  All servers closed. Cleaning up active connections... - [INFO] Stopping HttpProxy server  - [INFO] Closing 0 WebSocket connections  - [INFO] Function cache cleared  - [INFO] HttpProxy server stopped successfully  -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should defer certificate provisioning until after ports are listening (10ms) - Test starting: should have ACME challenge route ready before certificate provisioning -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   WARN ->  1 configuration warnings found -   WARN ->  Port 8080 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 8080 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 8443 is used by 1 routes: test-route -   info:  SmartProxy starting with 1 ports: 8443 - [INFO] Route manager configured with 0 routes across 0 ports  - [INFO] Updated RouteManager with 0 routes  - [INFO] Loaded default certificates from filesystem (sync - deprecated)  - Initialized HttpProxy on port 8846 - [INFO] Updating route configurations (0 routes)  - [INFO] Route manager configured with 0 routes across 0 ports  - [INFO] Updated RouteManager with 0 routes  - HttpRouter initialized with 0 routes (0 unique hosts) - [INFO] HttpRouter initialized with 0 routes (0 unique hosts)  - HttpRouter initialized with 0 routes (0 unique hosts) - [INFO] HttpRouter initialized with 0 routes (0 unique hosts)  - [INFO] Route configuration updated with 0 routes  - [WARN] CertificateManager is deprecated - use SmartCertManager instead  - [WARN] updateRoutes is deprecated - use SmartCertManager instead  - [INFO] WebSocket handler initialized  - [INFO] HttpProxy started on port 8846  -   info:  SmartProxy -> OK: Now listening on port 8443 -   info:  Using top-level ACME configuration with email: test@test.local -   info:  Starting certificate provisioning now that ports are ready -  debug  MetricsCollector started -   info:  SmartProxy shutting down... -   info:  Certificate manager stopped -   info:  NFTablesManager stopped -  debug  Port 8443 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8443 -   info:  All servers closed. Cleaning up active connections... - [INFO] Stopping HttpProxy server  - [INFO] Closing 0 WebSocket connections  - [INFO] Function cache cleared  - [INFO] HttpProxy server stopped successfully  -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should have ACME challenge route ready before certificate provisioning (103ms) - Summary: 2/2 PASSED in 2.4s - -▢️ test/test.certificate-acme-update.ts (18/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: SmartCertManager should call getCertificateForDomain with wildcard option - Testing SmartCertManager with SmartAcme v8.0.0 API... - Domain: example.com, DNS-01: true, Should include wildcard: true - Domain: example.com, DNS-01: false, Should include wildcard: false - Domain: sub.example.com, DNS-01: true, Should include wildcard: true - Domain: sub.example.com, DNS-01: false, Should include wildcard: false - Domain: *.example.com, DNS-01: true, Should include wildcard: false - Domain: *.example.com, DNS-01: false, Should include wildcard: false - Domain: test, DNS-01: true, Should include wildcard: false - Domain: test, DNS-01: false, Should include wildcard: false - Domain: my.sub.example.com, DNS-01: true, Should include wildcard: true - Domain: my.sub.example.com, DNS-01: false, Should include wildcard: false - All wildcard logic tests passed! - βœ… SmartCertManager should call getCertificateForDomain with wildcard option (1ms) - Summary: 1/1 PASSED in 2.3s - -▢️ test/test.certificate-provision.ts (19/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: SmartProxy should support custom certificate provision function -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes - βœ… SmartProxy should support custom certificate provision function (2ms) - Test starting: Custom certificate provision function should be called -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   WARN ->  1 configuration warnings found -   WARN ->  Port 9080 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 9080 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 9443 is used by 1 routes: custom-cert-route -   info:  SmartProxy starting with 1 ports: 9443 -   info:  SmartProxy -> OK: Now listening on port 9443 -   info:  Using top-level ACME configuration with email: test@example.com -  debug  Route update callback set successfully - ❌ Custom certificate provision function should be called (0ms) - ⟦TSTEST:META:{"time":1375,"retry":0,"error":{"message":"Error validating contact(s) :: contact email has forbidden domain \"example.com\"","stack":"Error: Error validating contact(s) :: contact email has forbidden domain \"example.com\"\n at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22)\n at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26)\n at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5)\n at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7)\n at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5)\n at async SmartProxy.testProxy2.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:129:25)\n at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24)\n at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 2, - "error": { - "message": "Error validating contact(s) :: contact email has forbidden domain \"example.com\"", - "stack": "Error: Error validating contact(s) :: contact email has forbidden domain \"example.com\"\n at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22)\n at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26)\n at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5)\n at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7)\n at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5)\n at async SmartProxy.testProxy2.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:129:25)\n at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24)\n at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5)" - } - } - Error details: - Error: Error validating contact(s) :: contact email has forbidden domain "example.com" - at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22) - at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26) - at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5) - at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7) - at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5) - at async SmartProxy.testProxy2.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:129:25) - at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24) - at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5) - Error: Error validating contact(s) :: contact email has forbidden domain "example.com" - at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22) - at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26) - at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5) - at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7) - at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5) - at async SmartProxy.testProxy2.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:129:25) - at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24) - at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5) - Test starting: Should fallback to ACME when custom provision fails -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   WARN ->  1 configuration warnings found -   WARN ->  Port 9080 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 9080 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 9444 is used by 1 routes: fallback-route -   info:  SmartProxy starting with 1 ports: 9444 -   info:  SmartProxy -> OK: Now listening on port 9444 -   info:  Using top-level ACME configuration with email: test@example.com -  debug  Route update callback set successfully - ❌ Should fallback to ACME when custom provision fails (0ms) - ⟦TSTEST:META:{"time":1092,"retry":0,"error":{"message":"Error validating contact(s) :: contact email has forbidden domain \"example.com\"","stack":"Error: Error validating contact(s) :: contact email has forbidden domain \"example.com\"\n at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22)\n at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26)\n at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5)\n at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7)\n at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5)\n at async SmartProxy.testProxy3.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:191:25)\n at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24)\n at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 3, - "error": { - "message": "Error validating contact(s) :: contact email has forbidden domain \"example.com\"", - "stack": "Error: Error validating contact(s) :: contact email has forbidden domain \"example.com\"\n at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22)\n at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26)\n at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5)\n at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7)\n at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5)\n at async SmartProxy.testProxy3.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:191:25)\n at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24)\n at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5)" - } - } - Error details: - Error: Error validating contact(s) :: contact email has forbidden domain "example.com" - at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22) - at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26) - at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5) - at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7) - at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5) - at async SmartProxy.testProxy3.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:191:25) - at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24) - at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5) - Error: Error validating contact(s) :: contact email has forbidden domain "example.com" - at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22) - at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26) - at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5) - at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7) - at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5) - at async SmartProxy.testProxy3.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:191:25) - at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24) - at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5) - Test starting: Should not fallback when certProvisionFallbackToAcme is false -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   WARN ->  2 configuration warnings found -   WARN ->  Routes with certificate: "auto" require ACME email configuration. Add email to either top-level "acme" config or individual route's "tls.acme" config. -   WARN ->  Port 80 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 80 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 9445 is used by 1 routes: no-fallback-route -   info:  SmartProxy starting with 1 ports: 9445 -   info:  SmartProxy -> OK: Now listening on port 9445 - ❌ Should not fallback when certProvisionFallbackToAcme is false (0ms) - ⟦TSTEST:META:{"time":1,"retry":0,"error":{"message":"Expected value to be true","stack":"Error: Expected value to be true\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27)\n at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:273:23)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20)\n at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 4, - "error": { - "message": "Expected value to be true", - "stack": "Error: Expected value to be true\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27)\n at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:273:23)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20)\n at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13)" - } - } - Error details: - Error: Expected value to be true - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27) - at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:273:23) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20) - at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13) - Error: Expected value to be true - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27) - at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:273:23) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20) - at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13) - Test starting: Should return http01 for unknown domains -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   WARN ->  1 configuration warnings found -   WARN ->  Port 9081 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 9081 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 9446 is used by 1 routes: unknown-domain-route -   info:  SmartProxy starting with 1 ports: 9446 -   info:  SmartProxy -> OK: Now listening on port 9446 -   info:  Using top-level ACME configuration with email: test@example.com -  debug  Route update callback set successfully - ❌ Should return http01 for unknown domains (0ms) - ⟦TSTEST:META:{"time":963,"retry":0,"error":{"message":"Error validating contact(s) :: contact email has forbidden domain \"example.com\"","stack":"Error: Error validating contact(s) :: contact email has forbidden domain \"example.com\"\n at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22)\n at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26)\n at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5)\n at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7)\n at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5)\n at async SmartProxy.testProxy5.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:329:25)\n at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24)\n at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 5, - "error": { - "message": "Error validating contact(s) :: contact email has forbidden domain \"example.com\"", - "stack": "Error: Error validating contact(s) :: contact email has forbidden domain \"example.com\"\n at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22)\n at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26)\n at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5)\n at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7)\n at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5)\n at async SmartProxy.testProxy5.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:329:25)\n at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24)\n at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5)" - } - } - Error details: - Error: Error validating contact(s) :: contact email has forbidden domain "example.com" - at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22) - at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26) - at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5) - at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7) - at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5) - at async SmartProxy.testProxy5.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:329:25) - at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24) - at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5) - Error: Error validating contact(s) :: contact email has forbidden domain "example.com" - at AcmeApi.apiRequest (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:53:19) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async AcmeApi.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/api.js:99:22) - at async AcmeClient.createAccount (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/acme-client@5.4.0/node_modules/acme-client/src/client.js:180:26) - at async SmartAcme.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartacme@8.0.0_@aws-sdk+credential-providers@3.798.0_socks@2.8.4/node_modules/@push.rocks/smartacme/ts/smartacme.classes.smartacme.ts:142:5) - at async SmartCertManager.initialize (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/certificate-manager.ts:153:7) - at async SmartProxy.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:256:5) - at async SmartProxy.testProxy5.createCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/test/test.certificate-provision.ts:329:25) - at async SmartProxy.initializeCertificateManager (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:313:24) - at async SmartProxy.start (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/smart-proxy.ts:386:5) - Test starting: cleanup -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… cleanup (1ms) - Test 2 failed with status error: - || Custom certificate provision function should be called - || for more information please take a look the logs above - Test 3 failed with status error: - || Should fallback to ACME when custom provision fails - || for more information please take a look the logs above - Test 4 failed with status error: - || Should not fallback when certProvisionFallbackToAcme is false - || for more information please take a look the logs above - Test 5 failed with status error: - || Should return http01 for unknown domains - || for more information please take a look the logs above -Running tree kill with SIGTERM on process 831913 - ❌ Test file timeout (60000ms) - Error: Test file exceeded timeout of 60 seconds - Error details: - Test execution was terminated after 60 seconds -Running tree kill with SIGKILL on process 831913 - -⚠️ Error - Only 7 out of 6 completed! - -⚠️ Error - The amount of received tests and expectedTests is unequal! Therefore the testfile failed - Summary: 2 passed, 5 failed of 7 tests in 60.1s - -⚠️ Error - Only 7 out of 6 completed! - -⚠️ Error - The amount of received tests and expectedTests is unequal! Therefore the testfile failed - Summary: 2 passed, 5 failed of 7 tests in 60.1s - -▢️ test/test.certificate-provisioning.ts (20/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes - Test starting: should provision certificate automatically -   WARN ->  1 configuration warnings found -   WARN ->  Port 9080 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 9080 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 9443 is used by 1 routes: test-route -   info:  SmartProxy starting with 1 ports: 9443 -   info:  SmartProxy -> OK: Now listening on port 9443 -   info:  Using route-level ACME configuration from route 'test-route' with email: test@test.local -   info:  Starting certificate provisioning now that ports are ready -  debug  MetricsCollector started -   info:  SmartProxy shutting down... -   info:  Certificate manager stopped -   info:  NFTablesManager stopped -  debug  Port 9443 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 9443 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should provision certificate automatically (4ms) - Test starting: should handle static certificates -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 9444 is used by 1 routes: static-route -   info:  SmartProxy starting with 1 ports: 9444 -   info:  SmartProxy -> OK: Now listening on port 9444 -  debug  Route update callback set successfully -   info:  Certificate manager initialized. Deferring certificate provisioning until after ports are listening. -   info:  Starting certificate provisioning now that ports are ready -   WARN ->  HttpProxy not set, cannot apply certificate for domain static.example.com -   info:  Successfully loaded static certificate for static.example.com -  debug  MetricsCollector started -   info:  SmartProxy shutting down... -   info:  Certificate manager stopped -   info:  NFTablesManager stopped -  debug  Port 9444 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 9444 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should handle static certificates (4ms) - Test starting: should handle ACME challenge routes -   info:  Route manager configured with 2 routes across 2 ports -   info:  Updated RouteManager with 2 routes -  debug  Port 9445 is used by 1 routes: auto-cert-route -  debug  Port 9081 is used by 1 routes: port-9081-route -   info:  SmartProxy starting with 2 ports: 9445, 9081 -   info:  SmartProxy -> OK: Now listening on port 9445 -   info:  SmartProxy -> OK: Now listening on port 9081 -   info:  Using route-level ACME configuration from route 'auto-cert-route' with email: acme@test.local -   info:  Starting certificate provisioning now that ports are ready -  debug  MetricsCollector started -   info:  SmartProxy shutting down... -   info:  Certificate manager stopped -   info:  NFTablesManager stopped -  debug  Port 9445 reference count decreased to 0 -  debug  Port 9081 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 9445 -   info:  SmartProxy -> Stopped listening on port 9081 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should handle ACME challenge routes (1ms) - Test starting: should renew certificates -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   WARN ->  1 configuration warnings found -   WARN ->  Port 9082 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 9082 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 9446 is used by 1 routes: renew-route -   info:  SmartProxy starting with 1 ports: 9446 -   info:  SmartProxy -> OK: Now listening on port 9446 -   info:  Using route-level ACME configuration from route 'renew-route' with email: renew@test.local -   info:  Starting certificate provisioning now that ports are ready -  debug  MetricsCollector started -   info:  SmartProxy shutting down... -   info:  Certificate manager stopped -   info:  NFTablesManager stopped -  debug  Port 9446 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 9446 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should renew certificates (1ms) - Summary: 4/4 PASSED in 2.3s - -▢️ test/test.certificate-simple.ts (21/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should create SmartProxy with certificate routes -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes - βœ… should create SmartProxy with certificate routes (2ms) - Test starting: should handle socket handler route type -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes - βœ… should handle socket handler route type (0ms) - Summary: 2/2 PASSED in 2.3s - -▢️ test/test.cleanup-queue-bug.node.ts (22/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: cleanup queue bug - verify queue processing handles more than batch size -  - === Cleanup Queue Bug Test === - Purpose: Verify that the cleanup queue correctly processes all connections - even when there are more than the batch size (100) -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8588 is used by 1 routes: test-route -   info:  SmartProxy starting with 1 ports: 8588 -   info:  SmartProxy -> OK: Now listening on port 8588 -   info:  No routes require certificate management -  debug  MetricsCollector started - βœ“ Proxy started on port 8588 -  - --- Creating 150 mock connections --- - Created 150 mock connections -  - --- Queueing all connections for cleanup --- -   info:  Cleaned up 50 connections -   info:  Cleaned up 50 connections -   info:  [SUMMARY] 50 connections terminated in 0s -   info:  [SUMMARY] 50 connections terminated in 0s -   info:  Cleaned up 50 connections - Cleanup queue size after queueing: 50 - Active connections after initial batch: 50 -  - --- Waiting for remaining cleanup batches to process --- -   info:  [SUMMARY] 50 connections terminated in 0s - Active connections: 0, Queue size: 0 - All cleanup completed in 100ms -  - Final connection count: 0 - Final cleanup queue size: 0 - Termination stats: { incoming: { test_cleanup: 150 }, outgoing: {} } -  - --- Stopping proxy --- -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8588 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8588 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. -  - βœ“ Test complete: Cleanup queue now correctly processes all connections - βœ… cleanup queue bug - verify queue processing handles more than batch size (111ms) - Summary: 1/1 PASSED in 2.5s - -▢️ test/test.connect-disconnect-cleanup.node.ts (23/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should handle clients that connect and immediately disconnect without sending data -  - === Testing Connect-Disconnect Cleanup === -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8560 is used by 1 routes: test-route -   info:  SmartProxy starting with 1 ports: 8560 -   info:  SmartProxy -> OK: Now listening on port 8560 -   info:  No routes require certificate management -  debug  MetricsCollector started - βœ“ Proxy started on port 8560 - Initial connection count: 0 -  - --- Test 1: Immediate disconnect --- -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection z4g8slckt7vtyuodqbii closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for z4g8slckt7vtyuodqbii -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection 7fx7llyal68bm7to9p86ej closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for 7fx7llyal68bm7to9p86ej -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection f1ziq2h21or2yp0u5v4ddm closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for f1ziq2h21or2yp0u5v4ddm -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection zukzivxcejd2t333sl0xt closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for zukzivxcejd2t333sl0xt -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection v43801kx99zqp24ok4tzi closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for v43801kx99zqp24ok4tzi - After 5 connect/disconnect cycles: 0 active connections -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection 9obb1kcen3z1immr11gz9 closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for 9obb1kcen3z1immr11gz9 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection ekriyzqdok8tcdzpt2ea1 closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for ekriyzqdok8tcdzpt2ea1 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection o7md8vzt7fez3a8jz53a closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for o7md8vzt7fez3a8jz53a -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection hben6esak3oxzttf8y37s closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for hben6esak3oxzttf8y37s -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -  debug  Connection y9iq1yeg7vun2d8piu36j closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for y9iq1yeg7vun2d8piu36j - After 10 connect/disconnect cycles: 0 active connections - After immediate disconnect test: 0 active connections -  - --- Test 2: Delayed disconnect --- - During delayed disconnect test: 0 active connections -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for kfngu0xyiejc5krrakogw9 to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection kfngu0xyiejc5krrakogw9: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection kfngu0xyiejc5krrakogw9 closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 36ys39edifqz65pj2czir to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 36ys39edifqz65pj2czir: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 36ys39edifqz65pj2czir closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for u28bqn0f6akcvxj3kao3 to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection u28bqn0f6akcvxj3kao3: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection u28bqn0f6akcvxj3kao3 closed during immediate routing: immediate-route-client_closed - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 9jrlpx2i0z7pqht1nuevc to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 9jrlpx2i0z7pqht1nuevc: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 9jrlpx2i0z7pqht1nuevc closed during immediate routing: immediate-route-client_closed -   ERROR!  Connection setup error for fvsdatr60nj7gqyclah0h to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection fvsdatr60nj7gqyclah0h: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -  debug  Connection fvsdatr60nj7gqyclah0h closed during immediate routing: immediate-route-client_closed - After delayed disconnect test: 0 active connections -  - --- Test 3: Mixed disconnect patterns --- -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 1 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -  debug  Connection jpe2u6zwa7rrkbdg9wac8k closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for jpe2u6zwa7rrkbdg9wac8k -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -  debug  Connection skmr4n5dktqg8e0k9q8d closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for skmr4n5dktqg8e0k9q8d -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for ph3gosnyrvfrruh227o to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection ph3gosnyrvfrruh227o: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection jz3my9vq4skyvg3lbmxdk closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for jz3my9vq4skyvg3lbmxdk -  debug  Connection ph3gosnyrvfrruh227o closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 3ntc1ewmtshoeg2c7d74n to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 3ntc1ewmtshoeg2c7d74n: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 3ntc1ewmtshoeg2c7d74n closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 2e8c58q77imfckvw0hqcq to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 2e8c58q77imfckvw0hqcq: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection fjbx4c0duxitefvomsl0lf closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for fjbx4c0duxitefvomsl0lf -  debug  Connection 2e8c58q77imfckvw0hqcq closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 -   ERROR!  Connection setup error for vl6gtrry9hobgqqrlzcokr to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection vl6gtrry9hobgqqrlzcokr: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -  debug  Connection lo9in7x1bwfdaxqe52lf0p closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for lo9in7x1bwfdaxqe52lf0p -  debug  Connection vl6gtrry9hobgqqrlzcokr closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for v2w1xbhsa4sfhtqkfu3bd to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection v2w1xbhsa4sfhtqkfu3bd: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 0724p3xmk5mcquh3rm6zlre closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for 0724p3xmk5mcquh3rm6zlre -  debug  Connection v2w1xbhsa4sfhtqkfu3bd closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for s6m8rip71fbja60vbcfr7 to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection s6m8rip71fbja60vbcfr7: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 234angsfx56jtq7cnuvq7l closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for 234angsfx56jtq7cnuvq7l -  debug  Connection s6m8rip71fbja60vbcfr7 closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 35hof8ei6pzndz4r98hqc to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 35hof8ei6pzndz4r98hqc: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 1cx5822jzdh5kew1wpla8m closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for 1cx5822jzdh5kew1wpla8m -  debug  Connection 35hof8ei6pzndz4r98hqc closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for crrxjxn1e1qoz1ojdwhwj9 to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection crrxjxn1e1qoz1ojdwhwj9: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection cqvb8pv67tzldv8e1zu7 closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for cqvb8pv67tzldv8e1zu7 -  debug  Connection crrxjxn1e1qoz1ojdwhwj9 closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8560. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for dg3k64rc6kkulpjqpkutom to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection dg3k64rc6kkulpjqpkutom: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -   ERROR!  Connection setup error for 26iizwpc0xder7cv3h20t to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 26iizwpc0xder7cv3h20t: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -  debug  Connection 26iizwpc0xder7cv3h20t closed during immediate routing: immediate-route-client_closed -  debug  Connection dg3k64rc6kkulpjqpkutom closed during immediate routing: immediate-route-client_closed - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for ihnsujrjrehna6zyvgkqs to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection ihnsujrjrehna6zyvgkqs: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection ihnsujrjrehna6zyvgkqs closed during immediate routing: immediate-route-client_closed - During mixed test: 0 active connections -  - Final connection count: 0 -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8560 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8560 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  [SUMMARY] 35 HttpProxy connections terminated in 3s -   info:  SmartProxy shutdown complete. - βœ“ Proxy stopped -  - Max connection count during immediate disconnect test: 0 -  - βœ… PASS: Connect-disconnect cleanup working correctly! - βœ… should handle clients that connect and immediately disconnect without sending data (2627ms) - Test starting: should handle clients that error during connection -  - === Testing Connection Error Cleanup === -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8561 is used by 1 routes: test-route -   info:  SmartProxy starting with 1 ports: 8561 -   info:  SmartProxy -> OK: Now listening on port 8561 -   info:  No routes require certificate management -  debug  MetricsCollector started - βœ“ Proxy started on port 8561 - Initial connection count: 0 -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 1 -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 2 - βœ“ All error connections completed -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for wcpvigag82dadhm5ig21r to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection wcpvigag82dadhm5ig21r: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection wcpvigag82dadhm5ig21r closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for if9c8blul4bndfh8zoq0a to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection if9c8blul4bndfh8zoq0a: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection if9c8blul4bndfh8zoq0a closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for unnow458g1bm3793nte9r to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection unnow458g1bm3793nte9r: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection unnow458g1bm3793nte9r closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for ufpwhmocpxi6unyfljw229 to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection ufpwhmocpxi6unyfljw229: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection ufpwhmocpxi6unyfljw229 closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for o77vhbbwc8s4edyxazh9xz to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection o77vhbbwc8s4edyxazh9xz: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection o77vhbbwc8s4edyxazh9xz closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 57bi5afer5o0uj5ms0tu71h to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 57bi5afer5o0uj5ms0tu71h: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 57bi5afer5o0uj5ms0tu71h closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for ogu3nplgadgaaa0knvlv3e to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection ogu3nplgadgaaa0knvlv3e: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection ogu3nplgadgaaa0knvlv3e closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8561. Active connections: 3 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 3bf3u5awhvgwlappda8mg to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 3bf3u5awhvgwlappda8mg: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 3bf3u5awhvgwlappda8mg closed during immediate routing: immediate-route-client_closed - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 2fc87e59pmj563r5jv5wa to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 2fc87e59pmj563r5jv5wa: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 2fc87e59pmj563r5jv5wa closed during immediate routing: immediate-route-client_closed -   ERROR!  Connection setup error for 8jvy2k1kyo29pj8630i0ag to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection 8jvy2k1kyo29pj8630i0ag: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 8jvy2k1kyo29pj8630i0ag closed during immediate routing: immediate-route-client_closed - Final connection count: 0 -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8561 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8561 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  [SUMMARY] 10 HttpProxy connections terminated in 0s -   info:  SmartProxy shutdown complete. - βœ“ Proxy stopped -  - βœ… PASS: Connection error cleanup working correctly! - βœ… should handle clients that error during connection (509ms) - Summary: 2/2 PASSED in 33.9s - -▢️ test/test.connection-cleanup-comprehensive.node.ts (24/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: comprehensive connection cleanup test - all scenarios -  - === Comprehensive Connection Cleanup Test === -   info:  Route manager configured with 2 routes across 2 ports -   info:  Updated RouteManager with 2 routes -  debug  Port 8570 is used by 1 routes: non-tls-route -  debug  Port 8571 is used by 1 routes: tls-route -   info:  SmartProxy starting with 2 ports: 8570, 8571 -   info:  SmartProxy -> OK: Now listening on port 8570 -   info:  SmartProxy -> OK: Now listening on port 8571 -   info:  No routes require certificate management -  debug  MetricsCollector started - βœ“ Proxy started on ports 8570 (non-TLS) and 8571 (TLS) - Initial connection count: 0 -  - --- Test 1: Rapid ECONNREFUSED retries --- -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for zf6f23nln8evownmuo2k1g to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection zf6f23nln8evownmuo2k1g: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection zf6f23nln8evownmuo2k1g closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for c6qak6js5pogf1v4amna to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection c6qak6js5pogf1v4amna: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection c6qak6js5pogf1v4amna closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for msioo6bzopvcnbnvpm54p to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection msioo6bzopvcnbnvpm54p: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection msioo6bzopvcnbnvpm54p closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 7olpugixxsrw91hetit77j to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 7olpugixxsrw91hetit77j: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 7olpugixxsrw91hetit77j closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for huul5v518v6gzkzo7pxk5p to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection huul5v518v6gzkzo7pxk5p: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection huul5v518v6gzkzo7pxk5p closed during immediate routing: immediate-route-client_closed - After 5 ECONNREFUSED retries: 0 active connections -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for z5ieea1klitshovosek1k to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection z5ieea1klitshovosek1k: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection z5ieea1klitshovosek1k closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for fra671xpfuew0se7nanab to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection fra671xpfuew0se7nanab: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection fra671xpfuew0se7nanab closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 6xc11p6ix6amh852lhqbn to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 6xc11p6ix6amh852lhqbn: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 6xc11p6ix6amh852lhqbn closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for vkighf9nmdextd9gajxrc to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection vkighf9nmdextd9gajxrc: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection vkighf9nmdextd9gajxrc closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for buri0uelsbwmk7ywq6xh to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection buri0uelsbwmk7ywq6xh: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection buri0uelsbwmk7ywq6xh closed during immediate routing: immediate-route-client_closed - After 10 ECONNREFUSED retries: 0 active connections -  - --- Test 2: Connect without sending data --- -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection pcktfu7jn3khmjrewx9eir closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for pcktfu7jn3khmjrewx9eir -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection nd059veyl1jjad5qnwx4e closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for nd059veyl1jjad5qnwx4e -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection q9bvrdsedycgeckjm6h08 closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for q9bvrdsedycgeckjm6h08 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection k5r8box85qfogjagfcnl5s closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for k5r8box85qfogjagfcnl5s -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection gor7e04mnput16sizffvl closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for gor7e04mnput16sizffvl -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection zkeaje6668pnf6katww4bl closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for zkeaje6668pnf6katww4bl -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection msl8etyyajfl60cuyqo59 closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for msl8etyyajfl60cuyqo59 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection 5ikimuh068wmj04cuwzxnh closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for 5ikimuh068wmj04cuwzxnh -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection aqkayqlsyorlqvrwhlvp closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for aqkayqlsyorlqvrwhlvp -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection s2tozbqrogfz7aijs8vl6s closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for s2tozbqrogfz7aijs8vl6s - After connect-without-data test: 0 active connections -  - --- Test 3: TLS early disconnect --- -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection s1uagbohwgzwhqbep163 ended before sending initial data -   WARN ->  Connection s1uagbohwgzwhqbep163 closed before sending initial data -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection ttca3tp7gi32eje3eiu4 ended before sending initial data -   WARN ->  Connection ttca3tp7gi32eje3eiu4 closed before sending initial data -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection jl7jlfcvk5b8ogys1iupu ended before sending initial data -   WARN ->  Connection jl7jlfcvk5b8ogys1iupu closed before sending initial data -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection l0xx413gpheis6988yode ended before sending initial data -   WARN ->  Connection l0xx413gpheis6988yode closed before sending initial data -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection iz2cmbrr6j01hlfh0clbjj ended before sending initial data -   WARN ->  Connection iz2cmbrr6j01hlfh0clbjj closed before sending initial data -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection um5hc44ym8hedz6umy5qfk ended before sending initial data -   WARN ->  Connection um5hc44ym8hedz6umy5qfk closed before sending initial data -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection pg1i86etdhyyognu1pytq ended before sending initial data -   WARN ->  Connection pg1i86etdhyyognu1pytq closed before sending initial data -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection 568pppo5to7zkkrfat9u2f ended before sending initial data -   WARN ->  Connection 568pppo5to7zkkrfat9u2f closed before sending initial data -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection st1nzucdd3skx500qdrul ended before sending initial data -   WARN ->  Connection st1nzucdd3skx500qdrul closed before sending initial data -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -  debug  Connection 7xkb7ynlc7g06vxkck5d3x8 ended before sending initial data -   WARN ->  Connection 7xkb7ynlc7g06vxkck5d3x8 closed before sending initial data - After TLS early disconnect test: 0 active connections -  - --- Test 4: Mixed chaos pattern --- -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -  debug  Connection l5f47791p0g8chtide7ux2 closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for l5f47791p0g8chtide7ux2 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 1 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 3 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 4 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 5 -  debug  Connection pc0ke27lds2ryvh19nsok ended before sending initial data - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for hvd23lveq3ru5t0a0te38 to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection hvd23lveq3ru5t0a0te38: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -   ERROR!  Connection setup error for 8t5blofd7sp0j97z8euvj7r to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 8t5blofd7sp0j97z8euvj7r: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   WARN ->  Connection pc0ke27lds2ryvh19nsok closed before sending initial data -  debug  Connection 8t5blofd7sp0j97z8euvj7r closed during immediate routing: immediate-route-client_closed - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for b46chasw9pcntv4v39hkp to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection b46chasw9pcntv4v39hkp: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -   ERROR!  Connection setup error for bzxhmmcb9pqrhbyul1bd8 to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection bzxhmmcb9pqrhbyul1bd8: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection b46chasw9pcntv4v39hkp closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 1 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 3 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 4 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 5 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 8z65n3wlhhaqx4jtb4jxsg to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 8z65n3wlhhaqx4jtb4jxsg: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 8z65n3wlhhaqx4jtb4jxsg closed during immediate routing: immediate-route-client_closed - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for x9v1bkf1r6k36lwrzcdwnz to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection x9v1bkf1r6k36lwrzcdwnz: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 59j3o76c3at19p6v224er6 closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for 59j3o76c3at19p6v224er6 -  debug  Connection x9v1bkf1r6k36lwrzcdwnz closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 3 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 4 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 5 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 6 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 7 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for a1kvb83a4dbrt1q5zubuw to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection a1kvb83a4dbrt1q5zubuw: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 98e5dr4hjgtf37p95j5zcl ended before sending initial data -   WARN ->  Connection 98e5dr4hjgtf37p95j5zcl closed before sending initial data -  debug  Connection a1kvb83a4dbrt1q5zubuw closed during immediate routing: immediate-route-client_closed - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for j93pjbypycxr2c13vawc to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection j93pjbypycxr2c13vawc: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -   ERROR!  Connection setup error for chlcux73fcits9x1ddxbg to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection chlcux73fcits9x1ddxbg: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection chlcux73fcits9x1ddxbg closed during immediate routing: immediate-route-client_closed -   ERROR!  Connection setup error for dpurq2fol6gwu0el6obkec to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection dpurq2fol6gwu0el6obkec: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 3 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 4 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 5 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 6 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 7 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 9r7efkmf366mkcv2rmi2w to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 9r7efkmf366mkcv2rmi2w: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 9r7efkmf366mkcv2rmi2w closed during immediate routing: immediate-route-client_closed - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for o337cwrdg0pxh8n2emu4u to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection o337cwrdg0pxh8n2emu4u: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection u3cp5ya9d964zv4zu909a closed during immediate routing: immediate-route-client_closed -  debug  Destroying outgoing connection for u3cp5ya9d964zv4zu909a -  debug  Connection o337cwrdg0pxh8n2emu4u closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 5 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 6 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 7 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 8 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 9 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for o7uj8ag2w8olebkdlwygw to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection o7uj8ag2w8olebkdlwygw: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection o7uj8ag2w8olebkdlwygw closed during immediate routing: immediate-route-client_closed - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for g6itie3murvgxy1kt28qiq to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection g6itie3murvgxy1kt28qiq: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection gtizlh04w39j15vop7cju ended before sending initial data -   WARN ->  Connection gtizlh04w39j15vop7cju closed before sending initial data -   info:  [SUMMARY] 50 HttpProxy connections terminated in 1s -  debug  Connection g6itie3murvgxy1kt28qiq closed during immediate routing: immediate-route-client_closed -   ERROR!  Connection setup error for l2l5brkqaf3q21v4aww2h to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection l2l5brkqaf3q21v4aww2h: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for gcu0g564rz5rznhdyjt50i to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection gcu0g564rz5rznhdyjt50i: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 5 -   info:  New connection from ::ffff:127.0.0.1 on port 8570. Active connections: 6 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 7 -   info:  New connection from ::ffff:127.0.0.1 on port 8571. Active connections: 8 - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -   ERROR!  Connection setup error for 7dpd3osdzr5l37ynqa2m0o to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection 7dpd3osdzr5l37ynqa2m0o: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. -  debug  Connection 7dpd3osdzr5l37ynqa2m0o closed during immediate routing: immediate-route-client_closed -   ERROR!  Connection setup error for jy7uj3i4skevp71qswjlgf to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 (ECONNREFUSED) -   ERROR!  Connection jy7uj3i4skevp71qswjlgf: Target localhost:9999 refused connection. Check if the target service is running and listening on that port. - Socket connection error to localhost:9999: connect ECONNREFUSED 127.0.0.1:9999 -  debug  Connection jy7uj3i4skevp71qswjlgf closed during immediate routing: immediate-route-client_closed -  debug  Connection ozwwrsk6j5hb4pdsgz7f9e ended before sending initial data -   WARN ->  Connection ozwwrsk6j5hb4pdsgz7f9e closed before sending initial data -  debug  Connection wpro3jrix6tuyupb29j4 ended before sending initial data -   WARN ->  Connection wpro3jrix6tuyupb29j4 closed before sending initial data -  debug  Connection giss9kkg85syhf5ipnubbe ended before sending initial data -   WARN ->  Connection giss9kkg85syhf5ipnubbe closed before sending initial data -  debug  Connection heegnj1ldgej7jp6rdnbuj ended before sending initial data -   WARN ->  Connection heegnj1ldgej7jp6rdnbuj closed before sending initial data -  debug  Connection 20v5tvlfq85rm8j1t8urbe ended before sending initial data -   WARN ->  Connection 20v5tvlfq85rm8j1t8urbe closed before sending initial data - βœ“ Chaos test completed -  debug  Connection zj9rtxtlfj8gallvtwbiw ended before sending initial data -   WARN ->  Connection zj9rtxtlfj8gallvtwbiw closed before sending initial data - After chaos test: 0 active connections -  - --- Test 5: NFTables route cleanup --- -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8572 is used by 1 routes: nftables-route -   info:  SmartProxy starting with 1 ports: 8572 - [2025-07-20T13:16:32.571Z] [WARN] Command failed (attempt 1/3): nft list tables ip {"error":"Command failed: nft list tables ip\nOperation not permitted (you must be root)\nnetlink: Error: cache initialization failed: Operation not permitted\n"} - [2025-07-20T13:16:33.578Z] [WARN] Command failed (attempt 2/3): nft list tables ip {"error":"Command failed: nft list tables ip\nOperation not permitted (you must be root)\nnetlink: Error: cache initialization failed: Operation not permitted\n"} - [2025-07-20T13:16:34.585Z] [WARN] Command failed (attempt 3/3): nft list tables ip {"error":"Command failed: nft list tables ip\nOperation not permitted (you must be root)\nnetlink: Error: cache initialization failed: Operation not permitted\n"} - [2025-07-20T13:16:34.585Z] [ERROR] Failed to set up tables and chains: Failed after 3 attempts: Command failed: nft list tables ip - Operation not permitted (you must be root) - netlink: Error: cache initialization failed: Operation not permitted -  - Failed to provision NFTables rules for route nftables-route: Failed to set up nftables tables and chains -   info:  SmartProxy -> OK: Now listening on port 8572 -   info:  No routes require certificate management -  debug  MetricsCollector started -   info:  New connection from ::ffff:127.0.0.1 on port 8572. Active connections: 1 -   info:  NFTables forwarding -  debug  Connection bhuhcgca0gsxbqi086lpr closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8572. Active connections: 1 -   info:  NFTables forwarding -  debug  Connection y4bm3bpcdhjli7ac9b7tn closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8572. Active connections: 1 -   info:  NFTables forwarding -  debug  Connection elz81dgwu2j9qlehyg495u closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8572. Active connections: 1 -   info:  NFTables forwarding -  debug  Connection phgzjmpiqtjyeg6d39aps closed during immediate routing: immediate-route-client_closed -   info:  New connection from ::ffff:127.0.0.1 on port 8572. Active connections: 1 -   info:  NFTables forwarding -  debug  Connection c6ftdqzrgkinnfqeiyp8dp closed during immediate routing: immediate-route-client_closed - NFTables connections after test: 0 -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8572 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8572 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  [SUMMARY] 15 HttpProxy connections terminated in 5s -   info:  SmartProxy shutdown complete. -  - Final connection count: 0 -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8570 reference count decreased to 0 -  debug  Port 8571 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8570 -   info:  SmartProxy -> Stopped listening on port 8571 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ“ Proxy stopped -  - βœ… PASS: Comprehensive connection cleanup test passed! - All connection scenarios properly cleaned up: - - ECONNREFUSED rapid retries - - Connect without sending data - - TLS early disconnect - - Mixed chaos patterns - - NFTables connections - βœ… comprehensive connection cleanup test - all scenarios (5729ms) - [DEPRECATION WARNING] executeWithRetrySync blocks the event loop and should not be used. Consider using the async executeWithRetry method instead. - Operation not permitted (you must be root) - netlink: Error: cache initialization failed: Operation not permitted - [2025-07-20T13:17:01.043Z] [WARN] Command failed (attempt 1/3): nft list tables ip {"error":"Command failed: nft list tables ip\nOperation not permitted (you must be root)\nnetlink: Error: cache initialization failed: Operation not permitted\n"} - Operation not permitted (you must be root) - netlink: Error: cache initialization failed: Operation not permitted - [2025-07-20T13:17:02.049Z] [WARN] Command failed (attempt 2/3): nft list tables ip {"error":"Command failed: nft list tables ip\nOperation not permitted (you must be root)\nnetlink: Error: cache initialization failed: Operation not permitted\n"} - Operation not permitted (you must be root) - netlink: Error: cache initialization failed: Operation not permitted - [2025-07-20T13:17:03.054Z] [WARN] Command failed (attempt 3/3): nft list tables ip {"error":"Command failed: nft list tables ip\nOperation not permitted (you must be root)\nnetlink: Error: cache initialization failed: Operation not permitted\n"} - [2025-07-20T13:17:03.054Z] [ERROR] Error cleaning up tables: Failed after 3 attempts: Command failed: nft list tables ip - Operation not permitted (you must be root) - netlink: Error: cache initialization failed: Operation not permitted -  - Summary: 1/1 PASSED in 35.5s - -▢️ test/test.connection-forwarding.ts (25/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: setup test servers - TCP test server listening on port 7001 - TLS test server listening on port 7002 - βœ… setup test servers (7ms) - Test starting: should forward TCP connections correctly -   info:  Route manager configured with 1 routes across 1 ports -   info:  Port 8080: 1 routes (TCP Forward Route) -   info:  Updated RouteManager with 1 routes -  debug  Port 8080 is used by 1 routes: TCP Forward Route -   info:  SmartProxy starting with 1 ports: 8080 -   info:  SmartProxy -> OK: Now listening on port 8080 -   info:  No routes require certificate management -  debug  MetricsCollector started -  debug  MetricsCollector: New connection recorded -   info:  New connection from ::ffff:127.0.0.1 on port 8080. Keep-Alive: Enabled. Active connections: 1 -   info:  Route matched -  debug  Checking HttpProxy forwarding: port=8080, useHttpProxy=undefined, isHttpProxyPort=undefined, hasHttpProxy=false -   info:  Using basic forwarding to 127.0.0.1:7001 for connection nvh865fr17km2lugzckwm -   info:  Setting up direct connection nvh865fr17km2lugzckwm to 127.0.0.1:7001 - Connected to proxy -   info:  Connection nvh865fr17km2lugzckwm established to target 127.0.0.1:7001 -   info:  Connection established: ::ffff:127.0.0.1 -> 127.0.0.1:7001 - Received: Connected to TCP test server - TCP Echo: Hello from client -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8080 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8080 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should forward TCP connections correctly (12ms) - Test starting: should handle TLS passthrough correctly -   info:  Route manager configured with 1 routes across 1 ports -   info:  Port 8443: 1 routes (TLS Passthrough Route) -   info:  Updated RouteManager with 1 routes -  debug  Port 8443 is used by 1 routes: TLS Passthrough Route -   info:  SmartProxy starting with 1 ports: 8443 -   info:  SmartProxy -> OK: Now listening on port 8443 -   info:  No routes require certificate management -  debug  MetricsCollector started -  debug  Connection nvh865fr17km2lugzckwm closed during immediate routing: immediate-route-client_closed -  debug  MetricsCollector: New connection recorded -   info:  New connection from ::ffff:127.0.0.1 on port 8443. Keep-Alive: Enabled. Active connections: 1 -   info:  TLS connection with SNI -   info:  Route matched -   info:  Using TLS passthrough to 127.0.0.1:7002 for connection i25yix0vkufu0fkr8gr4tj -   info:  Setting up direct connection i25yix0vkufu0fkr8gr4tj to 127.0.0.1:7002 -   info:  Connection i25yix0vkufu0fkr8gr4tj established to target 127.0.0.1:7002 - [i25yix0vkufu0fkr8gr4tj] Forwarding 374 bytes of initial data to target -   info:  Connection established: ::ffff:127.0.0.1 -> 127.0.0.1:7002 (SNI: test.example.com) -   info:  TLS renegotiation handler installed for connection i25yix0vkufu0fkr8gr4tj with SNI test.example.com - Connected via TLS - TLS Received: Connected to TLS test server -  -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8443 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8443 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s -   info:  SmartProxy shutdown complete. - βœ… should handle TLS passthrough correctly (10ms) - Test starting: should handle SNI-based forwarding -   info:  Route manager configured with 2 routes across 1 ports -   info:  Port 8443: 2 routes (Domain A Route, Domain B Route) -   info:  Updated RouteManager with 2 routes -  debug  Port 8443 is used by 2 routes: Domain A Route, Domain B Route -   info:  SmartProxy starting with 1 ports: 8443 -   info:  SmartProxy -> OK: Now listening on port 8443 -   info:  No routes require certificate management -  debug  MetricsCollector started -  debug  MetricsCollector: New connection recorded -   info:  New connection from ::ffff:127.0.0.1 on port 8443. Keep-Alive: Enabled. Active connections: 1 -   info:  TLS connection with SNI -   info:  Route matched -   info:  Using TLS passthrough to 127.0.0.1:7002 for connection pz6gamc2wymn6s432i83z -   info:  Setting up direct connection pz6gamc2wymn6s432i83z to 127.0.0.1:7002 -   info:  Connection pz6gamc2wymn6s432i83z established to target 127.0.0.1:7002 - [pz6gamc2wymn6s432i83z] Forwarding 371 bytes of initial data to target -   info:  Connection established: ::ffff:127.0.0.1 -> 127.0.0.1:7002 (SNI: a.example.com) -   info:  TLS renegotiation handler installed for connection pz6gamc2wymn6s432i83z with SNI a.example.com - Connected to domain A - Domain A response: Connected to TLS test server -  -  debug  MetricsCollector: New connection recorded -   info:  New connection from ::ffff:127.0.0.1 on port 8443. Keep-Alive: Enabled. Active connections: 2 -   info:  TLS connection with SNI -   info:  Route matched -   info:  Using TLS passthrough to 127.0.0.1:7002 for connection crlvaparay7acwg2aghmsf -   info:  Setting up direct connection crlvaparay7acwg2aghmsf to 127.0.0.1:7002 -   info:  Connection crlvaparay7acwg2aghmsf established to target 127.0.0.1:7002 - [crlvaparay7acwg2aghmsf] Forwarding 371 bytes of initial data to target -   info:  Connection established: ::ffff:127.0.0.1 -> 127.0.0.1:7002 (SNI: b.example.com) -   info:  TLS renegotiation handler installed for connection crlvaparay7acwg2aghmsf with SNI b.example.com - Connected to domain B - Domain B response: Connected to TLS test server -  -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8443 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8443 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s -   info:  SmartProxy shutdown complete. - βœ… should handle SNI-based forwarding (10ms) - Test starting: cleanup - βœ… cleanup (0ms) - Summary: 5/5 PASSED in 2.3s - -▢️ test/test.connection-limits.node.ts (26/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: Setup test environment - [Test Server] Listening on localhost:5100 -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 5101 is used by 1 routes: test-route -   info:  SmartProxy starting with 1 ports: 5101 -   info:  SmartProxy -> OK: Now listening on port 5101 -   info:  No routes require certificate management -  debug  MetricsCollector started - βœ… Setup test environment (11ms) - Test starting: Per-IP connection limits -   info:  New connection from ::ffff:127.0.0.1 on port 5101. Active connections: 1 -   info:  New connection from ::ffff:127.0.0.1 on port 5101. Active connections: 2 -   info:  New connection from ::ffff:127.0.0.1 on port 5101. Active connections: 3 -   info:  Connection established: ::ffff:127.0.0.1 -> localhost:5100 - ❌ Per-IP connection limits (0ms) - ⟦TSTEST:META:{"time":8,"retry":0,"error":{"message":"Expected string to include \"ECONNRESET\"","stack":"Error: Expected string to include \"ECONNRESET\"\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at StringMatchers.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/string.ts:25:27)\n at Proxy.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:355:60)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.connection-limits.node.ts:125:25)\n at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20)\n at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13)\n at async Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:532:9)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 2, - "error": { - "message": "Expected string to include \"ECONNRESET\"", - "stack": "Error: Expected string to include \"ECONNRESET\"\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at StringMatchers.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/string.ts:25:27)\n at Proxy.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:355:60)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.connection-limits.node.ts:125:25)\n at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20)\n at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13)\n at async Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:532:9)" - } - } - Error details: - Error: Expected string to include "ECONNRESET" - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at StringMatchers.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/string.ts:25:27) - at Proxy.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:355:60) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.connection-limits.node.ts:125:25) - at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20) - at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13) - at async Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:532:9) - Error: Expected string to include "ECONNRESET" - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at StringMatchers.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/string.ts:25:27) - at Proxy.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:355:60) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.connection-limits.node.ts:125:25) - at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20) - at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13) - at async Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:532:9) - Test starting: Route-level connection limits -   info:  Connection established: ::ffff:127.0.0.1 -> localhost:5100 -   info:  Connection established: ::ffff:127.0.0.1 -> localhost:5100 - ❌ Route-level connection limits (0ms) - ⟦TSTEST:META:{"time":3,"retry":0,"error":{"message":"Expected string to include \"ECONNRESET\"","stack":"Error: Expected string to include \"ECONNRESET\"\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at StringMatchers.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/string.ts:25:27)\n at Proxy.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:355:60)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.connection-limits.node.ts:149:25)\n at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20)\n at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13)\n at async Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:532:9)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 3, - "error": { - "message": "Expected string to include \"ECONNRESET\"", - "stack": "Error: Expected string to include \"ECONNRESET\"\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at StringMatchers.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/string.ts:25:27)\n at Proxy.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:355:60)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.connection-limits.node.ts:149:25)\n at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20)\n at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13)\n at async Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:532:9)" - } - } - Error details: - Error: Expected string to include "ECONNRESET" - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at StringMatchers.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/string.ts:25:27) - at Proxy.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:355:60) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.connection-limits.node.ts:149:25) - at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20) - at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13) - at async Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:532:9) - Error: Expected string to include "ECONNRESET" - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at StringMatchers.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/string.ts:25:27) - at Proxy.toInclude (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:355:60) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.connection-limits.node.ts:149:25) - at async TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:20) - at async TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:179:13) - at async Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:532:9) - Test starting: Connection rate limiting - βœ… Connection rate limiting (160ms) - Test starting: HttpProxy per-IP validation - [INFO] Route manager configured with 0 routes across 0 ports  - [INFO] Updated RouteManager with 0 routes  - [WARN] CertificateManager is deprecated - use SmartCertManager instead  - [INFO] Loaded default certificates from filesystem (sync - deprecated)  - [INFO] WebSocket handler initialized  - [INFO] HttpProxy started on port 5102  -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 5101 reference count decreased to 0 -   WARN ->  [SUMMARY] Rejected 17 connections from 1 IPs in 5s (Maximum connections per IP (3) exceeded: 17) -Running tree kill with SIGTERM on process 833307 - ❌ Test file timeout (60000ms) - Error: Test file exceeded timeout of 60 seconds - Error details: - Test execution was terminated after 60 seconds -Running tree kill with SIGKILL on process 833307 - -⚠️ Error - Only 5 out of 8 completed! - -⚠️ Error - The amount of received tests and expectedTests is unequal! Therefore the testfile failed - Summary: 2 passed, 3 failed of 5 tests in 60.1s - -⚠️ Error - Only 5 out of 8 completed! - -⚠️ Error - The amount of received tests and expectedTests is unequal! Therefore the testfile failed - Summary: 2 passed, 3 failed of 5 tests in 60.1s - -▢️ test/test.fix-verification.ts (27/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should verify certificate manager callback is preserved on updateRoutes -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   WARN ->  1 configuration warnings found -   WARN ->  Port 18080 is not configured for any routes but is needed for ACME challenges. Add a route listening on port 18080 or ensure it's accessible for HTTP-01 challenges. -  debug  Port 18443 is used by 1 routes: cert-route -   info:  SmartProxy starting with 1 ports: 18443 -   info:  SmartProxy -> OK: Now listening on port 18443 -   info:  Using top-level ACME configuration with email: test@local.test -   info:  Starting certificate provisioning now that ports are ready -  debug  MetricsCollector started -   info:  Updating routes (1 routes) -  debug  Port 18443 is used by 1 routes: cert-route -  debug  Port 18444 is used by 1 routes: updated-route -  debug  Current listening ports: 18443 -  debug  Ports needed for new routes: 18444 -   info:  Port 18443 no longer has any associated routes, will be released -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -   info:  Releasing 1 orphaned ports: 18443 -  debug  Port 18443 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 18443 -   info:  Binding to 1 new ports: 18444 -   info:  SmartProxy -> OK: Now listening on port 18444 -  debug  ACME port 80 is not already in use by other routes -   info:  Challenge route successfully removed from routes -   info:  SmartProxy shutting down... -   info:  Certificate manager stopped -   info:  NFTablesManager stopped -  debug  Port 18444 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 18444 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - Fix verified: Certificate manager callback is preserved on updateRoutes - βœ… should verify certificate manager callback is preserved on updateRoutes (6ms) - Summary: 1/1 PASSED in 2.3s - -▢️ test/test.forwarding-fix-verification.ts (28/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: setup test server - Test server listening on port 6789 - βœ… setup test server (2ms) - Test starting: regular forward route should work correctly -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 7890 is used by 1 routes: Test Forward Route -   info:  SmartProxy starting with 1 ports: 7890 -   info:  SmartProxy -> OK: Now listening on port 7890 -   info:  No routes require certificate management -  debug  MetricsCollector started -   info:  New connection from ::ffff:127.0.0.1 on port 7890. Active connections: 1 - Client connected to proxy - Test server: Client connected -   info:  Connection established: ::ffff:127.0.0.1 -> localhost:6789 - Test server received: Test message -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 7890 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 7890 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… regular forward route should work correctly (16ms) - βœ… NFTables forward route should not terminate connections (requires root) (0ms) - Test skipped: Marked as skip - Test starting: cleanup -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… cleanup (0ms) -  debug  Connection n30849wpdo3seqc75cfma closed during immediate routing: immediate-route-client_closed - Test server: Client disconnected -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s - Summary: 4/4 PASSED in 2.4s - -▢️ test/test.forwarding-regression.ts (29/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: forward connections should not be immediately closed - Test server listening on port 9090 -   info:  Route manager configured with 1 routes across 1 ports -   info:  Port 8080: 1 routes (Forward Test Route) -   info:  Updated RouteManager with 1 routes -  debug  Port 8080 is used by 1 routes: Forward Test Route -   info:  SmartProxy starting with 1 ports: 8080 -   info:  SmartProxy -> OK: Now listening on port 8080 -   info:  No routes require certificate management -  debug  MetricsCollector started -  debug  MetricsCollector: New connection recorded -   info:  New connection from ::ffff:127.0.0.1 on port 8080. Keep-Alive: Enabled. Active connections: 1 -   info:  Route matched -  debug  Checking HttpProxy forwarding: port=8080, useHttpProxy=undefined, isHttpProxyPort=undefined, hasHttpProxy=false -   info:  Using basic forwarding to 127.0.0.1:9090 for connection zm56pjb04saqdwnsasl3i -   info:  Setting up direct connection zm56pjb04saqdwnsasl3i to 127.0.0.1:9090 - Client connected to proxy - Client connected to test server -   info:  Connection zm56pjb04saqdwnsasl3i established to target 127.0.0.1:9090 -   info:  Connection established: ::ffff:127.0.0.1 -> 127.0.0.1:9090 - Client received: Welcome from test server -  - Test server received: Hello from client - Client received: Echo: Hello from client -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8080 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8080 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… forward connections should not be immediately closed (211ms) -  debug  Connection zm56pjb04saqdwnsasl3i closed during immediate routing: immediate-route-client_closed - Client connection closed -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s - Summary: 1/1 PASSED in 2.5s - -▢️ test/test.forwarding.examples.ts (30/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: Route-based configuration examples - HTTP-only route created successfully: Basic HTTP Route - ❌ Route-based configuration examples (0ms) - ⟦TSTEST:META:{"time":1,"retry":0,"error":{"message":"Expected value to be true","stack":"Error: Expected value to be true\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27)\n at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.forwarding.examples.ts:49:68)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 1, - "error": { - "message": "Expected value to be true", - "stack": "Error: Expected value to be true\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27)\n at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.forwarding.examples.ts:49:68)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)" - } - } - Error details: - Error: Expected value to be true - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27) - at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.forwarding.examples.ts:49:68) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - Error: Expected value to be true - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27) - at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.forwarding.examples.ts:49:68) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - Test 1 failed with status error: - || Route-based configuration examples - || for more information please take a look the logs above - Summary: 0 passed, 1 failed of 1 tests in 2.3s - -▢️ test/test.forwarding.ts (31/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< - Test starting: Route Helpers - Create HTTP routes - βœ… Route Helpers - Create HTTP routes (1ms) - Test starting: Route Helpers - Create HTTPS terminate to HTTP routes - βœ… Route Helpers - Create HTTPS terminate to HTTP routes (0ms) - Test starting: Route Helpers - Create HTTPS passthrough routes - βœ… Route Helpers - Create HTTPS passthrough routes (1ms) - Test starting: Route Helpers - Create HTTPS to HTTPS routes - βœ… Route Helpers - Create HTTPS to HTTPS routes (0ms) - Test starting: Route Helpers - Create complete HTTPS server with redirect - βœ… Route Helpers - Create complete HTTPS server with redirect (0ms) - Summary: 5/5 PASSED in 2.3s - -▢️ test/test.forwarding.unit.ts (32/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< - Test starting: ForwardingHandlerFactory - apply defaults based on type - βœ… ForwardingHandlerFactory - apply defaults based on type (1ms) - Test starting: ForwardingHandlerFactory - factory function for handlers - βœ… ForwardingHandlerFactory - factory function for handlers (0ms) - Summary: 2/2 PASSED in 2.3s - -▢️ test/test.http-fix-unit.ts (33/78) - Runtime: node.js - Test starting: should forward non-TLS connections on HttpProxy ports - Using HttpProxy for non-TLS connection on port 8080 - Test passed: Non-TLS connections on HttpProxy ports are forwarded correctly - βœ… should forward non-TLS connections on HttpProxy ports (1ms) - Test starting: should use direct connection for non-HttpProxy ports - Using basic forwarding for port 8080 - Test passed: Non-HttpProxy ports use direct connection - βœ… should use direct connection for non-HttpProxy ports (0ms) - Test starting: should handle ACME HTTP-01 challenges on port 80 with HttpProxy - Using HttpProxy for ACME challenge on port 80 - Test passed: ACME HTTP-01 challenges on port 80 use HttpProxy - βœ… should handle ACME HTTP-01 challenges on port 80 with HttpProxy (0ms) - Summary: 3/3 PASSED in 1.2s - -▢️ test/test.http-fix-verification.ts (34/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should detect and forward non-TLS connections on useHttpProxy ports -   info:  New connection from 127.0.0.1 on port 8080. Active connections: 1 - ❌ should detect and forward non-TLS connections on useHttpProxy ports (0ms) - ⟦TSTEST:META:{"time":2,"retry":0,"error":{"message":"this.smartProxy.connectionManager.trackConnectionByRoute is not a function","stack":"TypeError: this.smartProxy.connectionManager.trackConnectionByRoute is not a function\n at RouteConnectionHandler.handleForwardAction (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:751:39)\n at RouteConnectionHandler.routeConnection (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:638:21)\n at RouteConnectionHandler.handleInitialData (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:231:12)\n at RouteConnectionHandler.handleConnection (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:170:10)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-fix-verification.ts:118:11)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-fix-verification.ts:252:20)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 1, - "error": { - "message": "this.smartProxy.connectionManager.trackConnectionByRoute is not a function", - "stack": "TypeError: this.smartProxy.connectionManager.trackConnectionByRoute is not a function\n at RouteConnectionHandler.handleForwardAction (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:751:39)\n at RouteConnectionHandler.routeConnection (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:638:21)\n at RouteConnectionHandler.handleInitialData (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:231:12)\n at RouteConnectionHandler.handleConnection (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:170:10)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-fix-verification.ts:118:11)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-fix-verification.ts:252:20)" - } - } - Error details: - TypeError: this.smartProxy.connectionManager.trackConnectionByRoute is not a function - at RouteConnectionHandler.handleForwardAction (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:751:39) - at RouteConnectionHandler.routeConnection (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:638:21) - at RouteConnectionHandler.handleInitialData (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:231:12) - at RouteConnectionHandler.handleConnection (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:170:10) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-fix-verification.ts:118:11) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-fix-verification.ts:252:20) - TypeError: this.smartProxy.connectionManager.trackConnectionByRoute is not a function - at RouteConnectionHandler.handleForwardAction (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:751:39) - at RouteConnectionHandler.routeConnection (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:638:21) - at RouteConnectionHandler.handleInitialData (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:231:12) - at RouteConnectionHandler.handleConnection (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:170:10) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-fix-verification.ts:118:11) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-fix-verification.ts:252:20) - Test starting: should handle TLS connections normally -   info:  New connection from 127.0.0.1 on port 443. Active connections: 1 - /mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:751 - this.smartProxy.connectionManager.trackConnectionByRoute(record.routeId, record.id); - ^ -  -  - TypeError: this.smartProxy.connectionManager.trackConnectionByRoute is not a function - at RouteConnectionHandler.handleForwardAction (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:751:39) - at RouteConnectionHandler.routeConnection (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:638:21) - at processInitialData (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:385:12) - at Object._dataHandler (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/smart-proxy/route-connection-handler.ts:446:7) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-fix-verification.ts:243:16) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) -  - Node.js v23.8.0 - -⚠️ Error - Only 1 out of 2 completed! - -⚠️ Error - The amount of received tests and expectedTests is unequal! Therefore the testfile failed - Summary: 0 passed, 1 failed of 1 tests in 2.2s - -▢️ test/test.http-forwarding-fix.ts (35/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should detect and forward non-TLS connections on HttpProxy ports -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8081 is used by 1 routes: test-http-forward -   info:  SmartProxy starting with 1 ports: 8081 - Mock: HttpProxyBridge initialized - Mock: HttpProxyBridge started -   info:  SmartProxy -> OK: Now listening on port 8081 (HttpProxy forwarding enabled) -   info:  No routes require certificate management -  debug  MetricsCollector started -  debug  MetricsCollector: New connection recorded -   info:  New connection from ::ffff:127.0.0.1 on port 8081. Keep-Alive: Enabled. Active connections: 1 -   info:  Route matched -  debug  Checking HttpProxy forwarding: port=8081, useHttpProxy=[8081], isHttpProxyPort=true, hasHttpProxy=true -   info:  Using HttpProxy for non-TLS connection jre2vz42dd8fwv54ytr2vu on port 8081 - Mock: Connection forwarded to HttpProxy with args: jre2vz42dd8fwv54ytr2vu on port: 8081 - Client connected to proxy on port 8081 -  debug  Connection jre2vz42dd8fwv54ytr2vu closed during immediate routing: immediate-route-client_closed - About to stop proxy... -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8081 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8081 -   info:  All servers closed. Cleaning up active connections... - Mock: HttpProxyBridge stopped -  debug  MetricsCollector stopped -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s -   info:  SmartProxy shutdown complete. - Proxy stopped - βœ… should detect and forward non-TLS connections on HttpProxy ports (267ms) - Test starting: should properly detect non-TLS connections on HttpProxy ports - Target server listening on port 8182 -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8082 is used by 1 routes: test-route -   info:  SmartProxy starting with 1 ports: 8082 - Mock: HttpProxyBridge initialized - Mock: HttpProxyBridge started -   info:  SmartProxy -> OK: Now listening on port 8082 (HttpProxy forwarding enabled) -   info:  No routes require certificate management -  debug  MetricsCollector started -   info:  New connection from ::ffff:127.0.0.1 on port 8082. Active connections: 1 - HttpProxy forward called with connectionId: 95zydwak23lsk0y0ozjkza - Connected to proxy -  debug  Connection 95zydwak23lsk0y0ozjkza closed during immediate routing: immediate-route-client_closed -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8082 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8082 -   info:  All servers closed. Cleaning up active connections... - Mock: HttpProxyBridge stopped -  debug  MetricsCollector stopped -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s -   info:  SmartProxy shutdown complete. - βœ… should properly detect non-TLS connections on HttpProxy ports (254ms) - Summary: 2/2 PASSED in 2.8s - -▢️ test/test.http-port8080-forwarding.ts (36/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should forward HTTP connections on port 8080 - Target server listening on port 8181 -   info:  Route manager configured with 1 routes across 1 ports -   info:  Port 8080: 1 routes (test-route) -   info:  Updated RouteManager with 1 routes -  debug  Port 8080 is used by 1 routes: test-route -   info:  SmartProxy starting with 1 ports: 8080 -   info:  SmartProxy -> OK: Now listening on port 8080 -   info:  No routes require certificate management -  debug  MetricsCollector started - Making HTTP request to proxy... -  debug  MetricsCollector: New connection recorded -   info:  New connection from ::ffff:127.0.0.1 on port 8080. Keep-Alive: Enabled. Active connections: 1 -   info:  Route matched -  debug  Checking HttpProxy forwarding: port=8080, useHttpProxy=undefined, isHttpProxyPort=undefined, hasHttpProxy=false -   info:  Using basic forwarding to localhost:8181 for connection y05lajr7vqjonay9ddvjr -   info:  Setting up direct connection y05lajr7vqjonay9ddvjr to localhost:8181 -   info:  Connection y05lajr7vqjonay9ddvjr established to target localhost:8181 -   info:  Connection established: ::ffff:127.0.0.1 -> localhost:8181 - Target server received: GET /.well-known/acme-challenge/test-token - Got response from proxy: 200 -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8080 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8080 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. -  debug  Connection y05lajr7vqjonay9ddvjr closed during immediate routing: immediate-route-client_closed - βœ… should forward HTTP connections on port 8080 (5023ms) - Test starting: should handle basic HTTP request forwarding - Target server listening on port 8182 -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8081 is used by 1 routes: simple-forward -   info:  SmartProxy starting with 1 ports: 8081 -   info:  SmartProxy -> OK: Now listening on port 8081 -   info:  No routes require certificate management -  debug  MetricsCollector started - Making HTTP request to proxy... -   info:  New connection from ::ffff:127.0.0.1 on port 8081. Active connections: 1 -   info:  Connection established: ::ffff:127.0.0.1 -> localhost:8182 - Target received: GET /test from test.local - Got response from proxy: 200 - Received data chunk: Hello from target -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8081 reference count decreased to 0 -   info:  [SUMMARY] 1 HttpProxy connections terminated in 5s -   info:  SmartProxy -> Stopped listening on port 8081 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. -  debug  Connection 7ao4scehajgaukg2zsboo closed during immediate routing: immediate-route-client_closed -   info:  [SUMMARY] 1 HttpProxy connections terminated in 0s - βœ… should handle basic HTTP request forwarding (5003ms) - Summary: 2/2 PASSED in 12.4s - -▢️ test/test.http-port8080-simple.ts (37/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: should handle ACME challenges on port 8080 with improved port binding intelligence - Target server listening on port 9001 - Creating SmartProxy with ACME port 8080... -   info:  Route manager configured with 2 routes across 2 ports -   info:  Port 9003: 1 routes (test-route) -   info:  Port 9009: 1 routes (http-route) -   info:  Updated RouteManager with 2 routes - Mocking certificate manager... - Starting SmartProxy... -  debug  Port 9003 is used by 1 routes: test-route -  debug  Port 9009 is used by 1 routes: http-route -   info:  SmartProxy starting with 2 ports: 9003, 9009 -   info:  SmartProxy -> OK: Now listening on port 9003 -   info:  SmartProxy -> OK: Now listening on port 9009 -   info:  Using top-level ACME configuration with email: test@example.com -   info:  Starting certificate provisioning now that ports are ready - Mock: Provisioning certificates -  debug  MetricsCollector started - Port binding attempts: [ 9003, 9009 ] - Actually bound ports: [ 9003, 9009 ] - Testing route update with port reuse... -   info:  Updating routes (3 routes) -  debug  Port 9003 is used by 1 routes: test-route -  debug  Port 9009 is used by 1 routes: http-route -  debug  Port 9003 is used by 1 routes: test-route -  debug  Port 9009 is used by 2 routes: http-route, additional-route -  debug  Current listening ports: 9003, 9009 -  debug  Ports needed for new routes: 9003, 9009 -   info:  Route manager configured with 3 routes across 2 ports -   info:  Port 9003: 1 routes (test-route) -   info:  Port 9009: 2 routes (http-route, additional-route) -   info:  Updated RouteManager with 3 routes -  debug  ACME port 9009 is already in use by other routes -   info:  Challenge route successfully removed from routes - Port binding attempts after update: [] - Bound ports after update: [ 9003, 9009 ] - Port binding intelligence verified successfully! - Cleaning up... -   info:  SmartProxy shutting down... -   info:  Certificate manager stopped -   info:  NFTablesManager stopped -  debug  Port 9003 reference count decreased to 0 -  debug  Port 9009 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 9003 -   info:  SmartProxy -> Stopped listening on port 9009 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. - βœ… should handle ACME challenges on port 8080 with improved port binding intelligence (8ms) - Summary: 1/1 PASSED in 2.3s - -▢️ test/test.http-proxy-security-limits.node.ts (38/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< - Test starting: Setup HttpProxy SecurityManager - βœ… Setup HttpProxy SecurityManager (0ms) - Test starting: HttpProxy IP connection tracking - βœ… HttpProxy IP connection tracking (1ms) - Test starting: HttpProxy connection rate limiting - ❌ HttpProxy connection rate limiting (0ms) - ⟦TSTEST:META:{"time":0,"retry":0,"error":{"message":"Expected value to be true","stack":"Error: Expected value to be true\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27)\n at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-proxy-security-limits.node.ts:51:28)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)"}}⟧ - ⟦TSTEST:ERROR⟧ - { - "testNumber": 3, - "error": { - "message": "Expected value to be true", - "stack": "Error: Expected value to be true\n at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15)\n at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16)\n at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5)\n at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17)\n at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27)\n at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43)\n at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-proxy-security-limits.node.ts:51:28)\n at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26)\n at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34)\n at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39)" - } - } - Error details: - Error: Expected value to be true - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27) - at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-proxy-security-limits.node.ts:51:28) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - Error: Expected value to be true - at (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:299:15) - at runDirectOrNegated (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:214:16) - at Proxy.runCheck (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:279:5) - at Proxy.customAssertion (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:294:17) - at BooleanMatchers.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/namespaces/boolean.ts:11:27) - at Proxy.toBeTrue (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@push.rocks+smartexpect@2.5.0/node_modules/@push.rocks/smartexpect/ts/smartexpect.classes.assertion.ts:337:43) - at (/mnt/data/lossless/push.rocks/smartproxy/test/test.http-proxy-security-limits.node.ts:51:28) - at TapTest.currentTest.testFunction (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:514:26) - at TapTest.run (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.taptest.ts:176:34) - at Tap.start (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/@git.zone+tstest@2.3.1_@aws-sdk+credential-providers@3.798.0_socks@2.8.4_typescript@5.8.3/node_modules/@git.zone/tstest/ts_tapbundle/tapbundle.classes.tap.ts:528:39) - Test starting: HttpProxy CLIENT_IP header handling - βœ… HttpProxy CLIENT_IP header handling (0ms) - Test starting: HttpProxy automatic cleanup - βœ… HttpProxy automatic cleanup (102ms) - Test starting: Cleanup HttpProxy SecurityManager - βœ… Cleanup HttpProxy SecurityManager (0ms) - Test 3 failed with status error: - || HttpProxy connection rate limiting - || for more information please take a look the logs above - Summary: 5 passed, 1 failed of 6 tests in 2.3s - -▢️ test/test.httpproxy.function-targets.ts (39/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: setup HttpProxy function-based targets test environment - [INFO] Route manager configured with 0 routes across 0 ports  - [INFO] Updated RouteManager with 0 routes  - [WARN] CertificateManager is deprecated - use SmartCertManager instead  - [INFO] Loaded default certificates from filesystem (sync - deprecated)  - [INFO] WebSocket handler initialized  - [INFO] HttpProxy started on port 0  - HttpProxy actual listening port: 44271 - βœ… setup HttpProxy function-based targets test environment (5ms) - Test starting: should support static host/port routes - [INFO] Updating route configurations (1 routes)  - [INFO] Route manager configured with 1 routes across 1 ports  - [INFO] Updated RouteManager with 1 routes  - HttpRouter initialized with 1 routes (1 unique hosts) - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - HttpRouter initialized with 1 routes (1 unique hosts) - [WARN] updateRoutes is deprecated - use SmartCertManager instead  - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - [INFO] Route configuration updated with 1 routes  - βœ… should support static host/port routes (56ms) - Test starting: should support function-based host - [INFO] Updating route configurations (1 routes)  - [INFO] Route manager configured with 1 routes across 1 ports  - [INFO] Updated RouteManager with 1 routes  - HttpRouter initialized with 1 routes (1 unique hosts) - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - HttpRouter initialized with 1 routes (1 unique hosts) - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - [WARN] updateRoutes is deprecated - use SmartCertManager instead  - [INFO] Route configuration updated with 1 routes  - βœ… should support function-based host (45ms) - Test starting: should support function-based port - [INFO] Updating route configurations (1 routes)  - [INFO] Route manager configured with 1 routes across 1 ports  - [INFO] Updated RouteManager with 1 routes  - HttpRouter initialized with 1 routes (1 unique hosts) - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - HttpRouter initialized with 1 routes (1 unique hosts) - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - [INFO] Route configuration updated with 1 routes  - [WARN] updateRoutes is deprecated - use SmartCertManager instead  - βœ… should support function-based port (44ms) - Test starting: should support function-based host AND port - [INFO] Updating route configurations (1 routes)  - [INFO] Route manager configured with 1 routes across 1 ports  - [INFO] Updated RouteManager with 1 routes  - HttpRouter initialized with 1 routes (1 unique hosts) - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - HttpRouter initialized with 1 routes (1 unique hosts) - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - [INFO] Route configuration updated with 1 routes  - [WARN] updateRoutes is deprecated - use SmartCertManager instead  - βœ… should support function-based host AND port (44ms) - Test starting: should support context-based routing with path - [INFO] Updating route configurations (1 routes)  - [INFO] Route manager configured with 1 routes across 1 ports  - [INFO] Updated RouteManager with 1 routes  - HttpRouter initialized with 1 routes (1 unique hosts) - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - HttpRouter initialized with 1 routes (1 unique hosts) - [INFO] HttpRouter initialized with 1 routes (1 unique hosts)  - [INFO] Route configuration updated with 1 routes  - [WARN] updateRoutes is deprecated - use SmartCertManager instead  - βœ… should support context-based routing with path (46ms) - Test starting: cleanup HttpProxy function-based targets test environment - Test server closed successfully - HTTP/2 test server closed successfully - Stopping HttpProxy... - [INFO] Stopping HttpProxy server  - [INFO] Closing 0 WebSocket connections  - [INFO] Function cache cleared  - [INFO] HttpProxy server stopped successfully  - HttpProxy stopped successfully - βœ… cleanup HttpProxy function-based targets test environment (2ms) - Summary: 7/7 PASSED in 2.5s - -▢️ test/test.httpproxy.ts (40/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: setup test environment - [TEST] Loading and validating certificates - [TEST] Certificates loaded and validated - [TEST SERVER] Creating WebSocket server - Test server listening on port 3100 - βœ… setup test environment (3ms) - Test starting: should create proxy instance - [INFO] Route manager configured with 0 routes across 0 ports  - [INFO] Updated RouteManager with 0 routes  - [WARN] CertificateManager is deprecated - use SmartCertManager instead  - [INFO] Loaded default certificates from filesystem (sync - deprecated)  - βœ… should create proxy instance (2ms) - Test starting: should create proxy instance with extended options - [INFO] Route manager configured with 0 routes across 0 ports  - [INFO] Updated RouteManager with 0 routes  - [WARN] CertificateManager is deprecated - use SmartCertManager instead  - [INFO] Loaded default certificates from filesystem (sync - deprecated)  - βœ… should create proxy instance with extended options (0ms) - Test starting: should start the proxy server - [INFO] Route manager configured with 0 routes across 0 ports  - [INFO] Updated RouteManager with 0 routes  - [INFO] Loaded default certificates from filesystem (sync - deprecated)  - [WARN] CertificateManager is deprecated - use SmartCertManager instead  - [INFO] Updating route configurations (1 routes)  - [INFO] Route manager configured with 1 routes across 1 ports  - [INFO] Updated RouteManager with 1 routes  - HttpRouter initialized with 1 routes (2 unique hosts) - [INFO] HttpRouter initialized with 1 routes (2 unique hosts)  - HttpRouter initialized with 1 routes (2 unique hosts) - [WARN] updateRoutes is deprecated - use SmartCertManager instead  - [INFO] HttpRouter initialized with 1 routes (2 unique hosts)  - [INFO] Route configuration updated with 1 routes  - [INFO] WebSocket handler initialized  - [INFO] HttpProxy started on port 3001  - βœ… should start the proxy server (4ms) - Test starting: should route HTTPS requests based on host header - [TEST] Making HTTPS request: { - hostname: 'localhost', - port: 3001, - path: '/', - method: 'GET', - headers: { host: 'push.rocks' } - } - [TEST SERVER] Received HTTP request: { - url: '/', - method: 'GET', - headers: { host: 'localhost:3100', connection: 'keep-alive' } - } - [TEST] Received HTTPS response: { - statusCode: 200, - headers: { - 'access-control-allow-origin': '*', - 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'access-control-allow-headers': 'Content-Type, Authorization', - 'access-control-max-age': '86400', - server: 'NetworkProxy', - 'content-type': 'text/plain', - date: 'Sun, 20 Jul 2025 13:18:47 GMT', - connection: 'keep-alive', - 'keep-alive': 'timeout=5', - 'transfer-encoding': 'chunked' - } - } - [TEST] Response completed: { data: 'Hello from test server!' } - βœ… should route HTTPS requests based on host header (56ms) - Test starting: should handle unknown host headers - [TEST] Making HTTPS request: { - hostname: 'localhost', - port: 3001, - path: '/', - method: 'GET', - headers: { host: 'unknown.host' } - } - [WARN] No route configuration for host: unknown.host  - [TEST] Received HTTPS response: { - statusCode: 404, - headers: { - 'access-control-allow-origin': '*', - 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'access-control-allow-headers': 'Content-Type, Authorization', - 'access-control-max-age': '86400', - server: 'NetworkProxy', - date: 'Sun, 20 Jul 2025 13:18:47 GMT', - connection: 'keep-alive', - 'content-length': '47' - } - } - [TEST] Response completed: { data: 'Not Found: No route configuration for this host' } - βœ… should handle unknown host headers (5ms) - Test starting: should support WebSocket connections - [TEST] Testing WebSocket connection - [TEST] Creating WebSocket to wss://localhost:3001/ with host header: push.rocks - [TEST] WebSocket connected - [TEST] Sending WebSocket message: Hello WebSocket! - [TEST SERVER] Received WebSocket upgrade request: { - url: '/', - method: 'GET', - headers: { - host: 'localhost:3100', - upgrade: 'websocket', - connection: 'Upgrade', - 'sec-websocket-key': 'ON2Sz/WSh791AGV0Ntb4Yw==', - 'sec-websocket-version': '13', - 'sec-websocket-protocol': undefined - } - } - [TEST SERVER] Handling WebSocket upgrade - Test server: WebSocket headers: [ - 'HTTP/1.1 101 Switching Protocols', - 'Upgrade: websocket', - 'Connection: Upgrade', - 'Sec-WebSocket-Accept: Mu1VkcnDG6ULmepX5/X6Uzq29XM=' - ] - [TEST SERVER] WebSocket connection upgraded - [TEST SERVER] WebSocket connection established: { - url: '/', - headers: { - host: 'localhost:3100', - upgrade: 'websocket', - connection: 'Upgrade', - 'sec-websocket-key': 'ON2Sz/WSh791AGV0Ntb4Yw==', - 'sec-websocket-version': '13', - 'sec-websocket-protocol': undefined - } - } - [TEST] No message received after 2 seconds - [TEST] WebSocket test error: Error: Message timeout - at Timeout._onTimeout (/mnt/data/lossless/push.rocks/smartproxy/test/test.httpproxy.ts:363:49) - at listOnTimeout (node:internal/timers:614:17) - at process.processTimers (node:internal/timers:549:7) - [TEST] WebSocket test failed, continuing with other tests - βœ… should support WebSocket connections (3010ms) - Test starting: should handle custom headers - [INFO] Adding default headers { 'X-Proxy-Header': 'test-value' } - [INFO] Updated default response headers  - [TEST] Making HTTPS request: { - hostname: 'localhost', - port: 3001, - path: '/', - method: 'GET', - headers: { host: 'push.rocks' } - } - [ERROR] Error closing wsOutgoing: TypeError: First argument must be a valid error code number - at Sender.close (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/ws@8.18.2/node_modules/ws/lib/sender.js:187:13) - at WebSocket.close (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/ws@8.18.2/node_modules/ws/lib/websocket.js:315:18) - at WebSocket. (/mnt/data/lossless/push.rocks/smartproxy/ts/proxies/http-proxy/websocket-handler.ts:503:26) - at WebSocket.emit (node:events:519:35) - at WebSocket.emitClose (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/ws@8.18.2/node_modules/ws/lib/websocket.js:272:10) - at TLSSocket.socketOnClose (/mnt/data/lossless/push.rocks/smartproxy/node_modules/.pnpm/ws@8.18.2/node_modules/ws/lib/websocket.js:1341:15) - at TLSSocket.emit (node:events:519:35) - at node:net:351:12 - at Socket.done (node:_tls_wrap:650:7) - at Object.onceWrapper (node:events:622:26) - [TEST SERVER] Received HTTP request: { - url: '/', - method: 'GET', - headers: { host: 'localhost:3100', connection: 'keep-alive' } - } - [TEST] Received HTTPS response: { - statusCode: 200, - headers: { - 'access-control-allow-origin': '*', - 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'access-control-allow-headers': 'Content-Type, Authorization', - 'access-control-max-age': '86400', - 'x-proxy-header': 'test-value', - server: 'NetworkProxy', - 'content-type': 'text/plain', - date: 'Sun, 20 Jul 2025 13:18:50 GMT', - connection: 'keep-alive', - 'keep-alive': 'timeout=5', - 'transfer-encoding': 'chunked' - } - } - [TEST] Response completed: { data: 'Hello from test server!' } - βœ… should handle custom headers (5ms) - Test starting: should handle CORS preflight requests - [TEST] Making HTTPS request: { - hostname: 'localhost', - port: 3001, - path: '/', - method: 'OPTIONS', - headers: { - host: 'push.rocks', - origin: 'https://example.com', - 'access-control-request-method': 'POST', - 'access-control-request-headers': 'content-type' - } - } - [TEST] Received HTTPS response: { - statusCode: 204, - headers: { - 'access-control-allow-origin': '*', - 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'access-control-allow-headers': 'Content-Type, Authorization', - 'access-control-max-age': '86400', - date: 'Sun, 20 Jul 2025 13:18:50 GMT', - connection: 'keep-alive' - } - } - [TEST] Response completed: { data: '' } - βœ… should handle CORS preflight requests (1ms) - Test starting: should track connections and metrics - βœ… should track connections and metrics (1ms) - Test starting: should update capacity settings - [INFO] Updated max connections to 2000  - [INFO] Updated keep-alive timeout to 60000ms  - [INFO] Updated connection pool size to 25  - βœ… should update capacity settings (0ms) - Test starting: should handle certificate requests - βœ… should handle certificate requests (0ms) - Test starting: should update certificates directly - [WARN] requestCertificate is deprecated - use SmartCertManager instead  - [INFO] Updating certificate for test.example.com  - [INFO] Certificate updated for test.example.com  - βœ… should update certificates directly (0ms) - Test starting: cleanup - [TEST] Starting cleanup - [TEST] Terminating 1 WebSocket clients - [TEST] Closing WebSocket server - [TEST SERVER] WebSocket connection closed: { code: 1006, reason: '', wasClean: false } - Test server: WebSocket server closed - [TEST] WebSocket server closed - [TEST] Closing test server - [TEST] Test server closed - [TEST] Stopping proxy - [INFO] Stopping HttpProxy server  - [INFO] Closing 0 WebSocket connections  - [INFO] Function cache cleared  - [INFO] HttpProxy server stopped successfully  - [TEST] Proxy stopped successfully - [TEST] Cleanup complete - βœ… cleanup (1ms) - [TEST] WebSocket server close timeout - [TEST] Test server close timeout - [TEST] Proxy stop timeout - Summary: 14/14 PASSED in 7.4s - -▢️ test/test.keepalive-support.node.ts (41/78) - Runtime: node.js - called svDb() on >SmartDataDbDoc._createdAt< - called svDb() on >SmartDataDbDoc._updatedAt< -   info:  Logger initialized - Test starting: keepalive support - verify keepalive connections are properly handled -  - === KeepAlive Support Test === - Purpose: Verify that keepalive connections are not prematurely cleaned up - βœ“ Echo backend started on port 9998 -  - --- Test 1: Standard KeepAlive Treatment --- -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8590 is used by 1 routes: keepalive-route -   info:  SmartProxy starting with 1 ports: 8590 -   info:  SmartProxy -> OK: Now listening on port 8590 -   info:  No routes require certificate management -  debug  MetricsCollector started - βœ“ Proxy with standard keepalive started on port 8590 -   info:  New connection from ::ffff:127.0.0.1 on port 8590. Active connections: 1 - Client connected -   info:  Connection established: ::ffff:127.0.0.1 -> localhost:9998 - Received echo: Hello keepalive - KeepAlive connection 0gfejvdhf245g3t9m4232f: hasKeepAlive=true -   WARN ->  Timeout event on outgoing keep-alive connection 0gfejvdhf245g3t9m4232f from ::ffff:127.0.0.1 after 1h. Connection preserved. -   WARN ->  Timeout event on incoming keep-alive connection 0gfejvdhf245g3t9m4232f from ::ffff:127.0.0.1 after 1h. Connection preserved. - Socket timeout: server - Socket timeout: immediate-route-client - Socket timeout: client - Connections after 6s wait: 1 -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8590 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8590 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. -  debug  Connection 0gfejvdhf245g3t9m4232f closed during immediate routing: immediate-route-client_closed - Backend socket error (expected during cleanup): ECONNRESET -  - --- Test 2: Extended KeepAlive Treatment --- -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8591 is used by 1 routes: keepalive-extended -   info:  SmartProxy starting with 1 ports: 8591 -   info:  SmartProxy -> OK: Now listening on port 8591 -   info:  No routes require certificate management -  debug  MetricsCollector started - βœ“ Proxy with extended keepalive started on port 8591 -   info:  New connection from ::ffff:127.0.0.1 on port 8591. Active connections: 1 - Client connected with extended timeout -   info:  Connection established: ::ffff:127.0.0.1 -> localhost:9998 - Extended connection assnnwem5hkzviq224859: hasKeepAlive=true, treatment=extended -   info:  [SUMMARY] 1 HttpProxy connections terminated in 4s - Connections after 3s (base timeout exceeded): 1 -   info:  SmartProxy shutting down... -   info:  NFTablesManager stopped -  debug  Port 8591 reference count decreased to 0 -   info:  SmartProxy -> Stopped listening on port 8591 -   info:  All servers closed. Cleaning up active connections... -  debug  MetricsCollector stopped -   info:  SmartProxy shutdown complete. -  debug  Connection assnnwem5hkzviq224859 closed during immediate routing: immediate-route-client_closed -  - --- Test 3: Immortal KeepAlive Treatment --- -   info:  Route manager configured with 1 routes across 1 ports -   info:  Updated RouteManager with 1 routes -  debug  Port 8592 is used by 1 routes: keepalive-immortal -   info:  SmartProxy starting with 1 ports: 8592 -   info:  SmartProxy -> OK: Now listening on port 8592 -   info:  No routes require certificate management -  debug  MetricsCollector started - βœ“ Proxy with immortal keepalive started on port 8592 -   info:  New connection from ::ffff:127.0.0.1 on port 8592. Active connections: 1 - Client connected with immortal treatment -   info:  Connection established: ::ffff:127.0.0.1 -> localhost:9998 diff --git a/test/test.forwarding.ts b/test/test.forwarding.ts index 3429866..e19eaf5 100644 --- a/test/test.forwarding.ts +++ b/test/test.forwarding.ts @@ -1,9 +1,6 @@ import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../ts/plugins.js'; -import type { IForwardConfig, TForwardingType } from '../ts/forwarding/config/forwarding-types.js'; -// First, import the components directly to avoid issues with compiled modules -import { ForwardingHandlerFactory } from '../ts/forwarding/factory/forwarding-factory.js'; // Import route-based helpers import { createHttpRoute, diff --git a/test/test.forwarding.unit.ts b/test/test.forwarding.unit.ts deleted file mode 100644 index e114d6f..0000000 --- a/test/test.forwarding.unit.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { tap, expect } from '@git.zone/tstest/tapbundle'; -import * as plugins from '../ts/plugins.js'; - -// First, import the components directly to avoid issues with compiled modules -import { ForwardingHandlerFactory } from '../ts/forwarding/factory/forwarding-factory.js'; -// Import route-based helpers from the correct location -import { - createHttpRoute, - createHttpsTerminateRoute, - createHttpsPassthroughRoute, - createHttpToHttpsRedirect, - createCompleteHttpsServer, - createLoadBalancerRoute -} from '../ts/proxies/smart-proxy/utils/route-patterns.js'; - -// Create helper functions for building forwarding configs -const helpers = { - httpOnly: () => ({ type: 'http-only' as const }), - tlsTerminateToHttp: () => ({ type: 'https-terminate-to-http' as const }), - tlsTerminateToHttps: () => ({ type: 'https-terminate-to-https' as const }), - httpsPassthrough: () => ({ type: 'https-passthrough' as const }) -}; - -tap.test('ForwardingHandlerFactory - apply defaults based on type', async () => { - // HTTP-only defaults - const httpConfig = { - type: 'http-only' as const, - target: { host: 'localhost', port: 3000 } - }; - - const httpWithDefaults = ForwardingHandlerFactory['applyDefaults'](httpConfig); - - expect(httpWithDefaults.port).toEqual(80); - expect(httpWithDefaults.socket).toEqual('/tmp/forwarding-http-only-80.sock'); - - // HTTPS passthrough defaults - const httpsPassthroughConfig = { - type: 'https-passthrough' as const, - target: { host: 'localhost', port: 443 } - }; - - const httpsPassthroughWithDefaults = ForwardingHandlerFactory['applyDefaults'](httpsPassthroughConfig); - - expect(httpsPassthroughWithDefaults.port).toEqual(443); - expect(httpsPassthroughWithDefaults.socket).toEqual('/tmp/forwarding-https-passthrough-443.sock'); -}); - -tap.test('ForwardingHandlerFactory - factory function for handlers', async () => { - // @todo Implement unit tests for ForwardingHandlerFactory - // These tests would need proper mocking of the handlers -}); - -export default tap.start(); \ No newline at end of file diff --git a/test/test.route-utils.ts b/test/test.route-utils.ts index 5d38511..7edfa97 100644 --- a/test/test.route-utils.ts +++ b/test/test.route-utils.ts @@ -47,7 +47,7 @@ import { addRateLimiting, addBasicAuth, addJwtAuth -} from '../ts/proxies/smart-proxy/utils/route-patterns.js'; +} from '../ts/proxies/smart-proxy/utils/route-helpers.js'; import type { IRouteConfig, diff --git a/ts/forwarding/config/forwarding-types.ts b/ts/forwarding/config/forwarding-types.ts deleted file mode 100644 index 1e4d8f4..0000000 --- a/ts/forwarding/config/forwarding-types.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type * as plugins from '../../plugins.js'; - -/** - * The primary forwarding types supported by SmartProxy - * Used for configuration compatibility - */ -export type TForwardingType = - | 'http-only' // HTTP forwarding only (no HTTPS) - | 'https-passthrough' // Pass-through TLS traffic (SNI forwarding) - | 'https-terminate-to-http' // Terminate TLS and forward to HTTP backend - | 'https-terminate-to-https'; // Terminate TLS and forward to HTTPS backend - -/** - * Event types emitted by forwarding handlers - */ -export enum ForwardingHandlerEvents { - CONNECTED = 'connected', - DISCONNECTED = 'disconnected', - ERROR = 'error', - DATA_FORWARDED = 'data-forwarded', - HTTP_REQUEST = 'http-request', - HTTP_RESPONSE = 'http-response', - CERTIFICATE_NEEDED = 'certificate-needed', - CERTIFICATE_LOADED = 'certificate-loaded' -} - -/** - * Base interface for forwarding handlers - */ -export interface IForwardingHandler extends plugins.EventEmitter { - initialize(): Promise; - handleConnection(socket: plugins.net.Socket): void; - handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void; -} - -// Route-based helpers are now available directly from route-patterns.ts -import { - createHttpRoute, - createHttpsTerminateRoute, - createHttpsPassthroughRoute, - createHttpToHttpsRedirect, - createCompleteHttpsServer, - createLoadBalancerRoute -} from '../../proxies/smart-proxy/utils/route-patterns.js'; - -export { - createHttpRoute, - createHttpsTerminateRoute, - createHttpsPassthroughRoute, - createHttpToHttpsRedirect, - createCompleteHttpsServer, - createLoadBalancerRoute -}; - -// Note: Legacy helper functions have been removed -// Please use the route-based helpers instead: -// - createHttpRoute -// - createHttpsTerminateRoute -// - createHttpsPassthroughRoute -// - createHttpToHttpsRedirect -import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js'; - -// For backward compatibility, kept only the basic configuration interface -export interface IForwardConfig { - type: TForwardingType; - target: { - host: string | string[]; - port: number | 'preserve' | ((ctx: any) => number); - }; - http?: any; - https?: any; - acme?: any; - security?: any; - advanced?: any; - [key: string]: any; -} \ No newline at end of file diff --git a/ts/forwarding/config/index.ts b/ts/forwarding/config/index.ts deleted file mode 100644 index 695971b..0000000 --- a/ts/forwarding/config/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Forwarding configuration exports - * - * Note: The legacy domain-based configuration has been replaced by route-based configuration. - * See /ts/proxies/smart-proxy/models/route-types.ts for the new route-based configuration. - */ - -export type { - TForwardingType, - IForwardConfig, - IForwardingHandler -} from './forwarding-types.js'; - -export { - ForwardingHandlerEvents -} from './forwarding-types.js'; - -// Import route helpers from route-patterns instead of deleted route-helpers -export { - createHttpRoute, - createHttpsTerminateRoute, - createHttpsPassthroughRoute, - createHttpToHttpsRedirect, - createCompleteHttpsServer, - createLoadBalancerRoute -} from '../../proxies/smart-proxy/utils/route-patterns.js'; \ No newline at end of file diff --git a/ts/forwarding/factory/forwarding-factory.ts b/ts/forwarding/factory/forwarding-factory.ts deleted file mode 100644 index 9ec97d6..0000000 --- a/ts/forwarding/factory/forwarding-factory.ts +++ /dev/null @@ -1,189 +0,0 @@ -import type { IForwardConfig } from '../config/forwarding-types.js'; -import { ForwardingHandler } from '../handlers/base-handler.js'; -import { HttpForwardingHandler } from '../handlers/http-handler.js'; -import { HttpsPassthroughHandler } from '../handlers/https-passthrough-handler.js'; -import { HttpsTerminateToHttpHandler } from '../handlers/https-terminate-to-http-handler.js'; -import { HttpsTerminateToHttpsHandler } from '../handlers/https-terminate-to-https-handler.js'; - -/** - * Factory for creating forwarding handlers based on the configuration type - */ -export class ForwardingHandlerFactory { - /** - * Create a forwarding handler based on the configuration - * @param config The forwarding configuration - * @returns The appropriate forwarding handler - */ - public static createHandler(config: IForwardConfig): ForwardingHandler { - // Create the appropriate handler based on the forwarding type - switch (config.type) { - case 'http-only': - return new HttpForwardingHandler(config); - - case 'https-passthrough': - return new HttpsPassthroughHandler(config); - - case 'https-terminate-to-http': - return new HttpsTerminateToHttpHandler(config); - - case 'https-terminate-to-https': - return new HttpsTerminateToHttpsHandler(config); - - default: - // Type system should prevent this, but just in case: - throw new Error(`Unknown forwarding type: ${(config as any).type}`); - } - } - - /** - * Apply default values to a forwarding configuration based on its type - * @param config The original forwarding configuration - * @returns A configuration with defaults applied - */ - public static applyDefaults(config: IForwardConfig): IForwardConfig { - // Create a deep copy of the configuration - const result: IForwardConfig = JSON.parse(JSON.stringify(config)); - - // Apply defaults based on forwarding type - switch (config.type) { - case 'http-only': - // Set defaults for HTTP-only mode - result.http = { - enabled: true, - ...config.http - }; - // Set default port and socket if not provided - if (!result.port) { - result.port = 80; - } - if (!result.socket) { - result.socket = `/tmp/forwarding-${config.type}-${result.port}.sock`; - } - break; - - case 'https-passthrough': - // Set defaults for HTTPS passthrough - result.https = { - forwardSni: true, - ...config.https - }; - // SNI forwarding doesn't do HTTP - result.http = { - enabled: false, - ...config.http - }; - // Set default port and socket if not provided - if (!result.port) { - result.port = 443; - } - if (!result.socket) { - result.socket = `/tmp/forwarding-${config.type}-${result.port}.sock`; - } - break; - - case 'https-terminate-to-http': - // Set defaults for HTTPS termination to HTTP - result.https = { - ...config.https - }; - // Support HTTP access by default in this mode - result.http = { - enabled: true, - redirectToHttps: true, - ...config.http - }; - // Enable ACME by default - result.acme = { - enabled: true, - maintenance: true, - ...config.acme - }; - // Set default port and socket if not provided - if (!result.port) { - result.port = 443; - } - if (!result.socket) { - result.socket = `/tmp/forwarding-${config.type}-${result.port}.sock`; - } - break; - - case 'https-terminate-to-https': - // Similar to terminate-to-http but with different target handling - result.https = { - ...config.https - }; - result.http = { - enabled: true, - redirectToHttps: true, - ...config.http - }; - result.acme = { - enabled: true, - maintenance: true, - ...config.acme - }; - // Set default port and socket if not provided - if (!result.port) { - result.port = 443; - } - if (!result.socket) { - result.socket = `/tmp/forwarding-${config.type}-${result.port}.sock`; - } - break; - } - - return result; - } - - /** - * Validate a forwarding configuration - * @param config The configuration to validate - * @throws Error if the configuration is invalid - */ - public static validateConfig(config: IForwardConfig): void { - // Validate common properties - if (!config.target) { - throw new Error('Forwarding configuration must include a target'); - } - - if (!config.target.host || (Array.isArray(config.target.host) && config.target.host.length === 0)) { - throw new Error('Target must include a host or array of hosts'); - } - - // Validate port if it's a number - if (typeof config.target.port === 'number') { - if (config.target.port <= 0 || config.target.port > 65535) { - throw new Error('Target must include a valid port (1-65535)'); - } - } else if (config.target.port !== 'preserve' && typeof config.target.port !== 'function') { - throw new Error('Target port must be a number, "preserve", or a function'); - } - - // Type-specific validation - switch (config.type) { - case 'http-only': - // HTTP-only needs http.enabled to be true - if (config.http?.enabled === false) { - throw new Error('HTTP-only forwarding must have HTTP enabled'); - } - break; - - case 'https-passthrough': - // HTTPS passthrough doesn't support HTTP - if (config.http?.enabled === true) { - throw new Error('HTTPS passthrough does not support HTTP'); - } - - // HTTPS passthrough doesn't work with ACME - if (config.acme?.enabled === true) { - throw new Error('HTTPS passthrough does not support ACME'); - } - break; - - case 'https-terminate-to-http': - case 'https-terminate-to-https': - // These modes support all options, nothing specific to validate - break; - } - } -} \ No newline at end of file diff --git a/ts/forwarding/factory/index.ts b/ts/forwarding/factory/index.ts deleted file mode 100644 index d496aa5..0000000 --- a/ts/forwarding/factory/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Forwarding factory implementations - */ - -export { ForwardingHandlerFactory } from './forwarding-factory.js'; \ No newline at end of file diff --git a/ts/forwarding/handlers/base-handler.ts b/ts/forwarding/handlers/base-handler.ts deleted file mode 100644 index 83ba0e6..0000000 --- a/ts/forwarding/handlers/base-handler.ts +++ /dev/null @@ -1,155 +0,0 @@ -import * as plugins from '../../plugins.js'; -import type { - IForwardConfig, - IForwardingHandler -} from '../config/forwarding-types.js'; -import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; - -/** - * Base class for all forwarding handlers - */ -export abstract class ForwardingHandler extends plugins.EventEmitter implements IForwardingHandler { - /** - * Create a new ForwardingHandler - * @param config The forwarding configuration - */ - constructor(protected config: IForwardConfig) { - super(); - } - - /** - * Initialize the handler - * Base implementation does nothing, subclasses should override as needed - */ - public async initialize(): Promise { - // Base implementation - no initialization needed - } - - /** - * Handle a new socket connection - * @param socket The incoming socket connection - */ - public abstract handleConnection(socket: plugins.net.Socket): void; - - /** - * Handle an HTTP request - * @param req The HTTP request - * @param res The HTTP response - */ - public abstract handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void; - - /** - * Get a target from the configuration, supporting round-robin selection - * @param incomingPort Optional incoming port for 'preserve' mode - * @returns A resolved target object with host and port - */ - protected getTargetFromConfig(incomingPort: number = 80): { host: string, port: number } { - const { target } = this.config; - - // Handle round-robin host selection - if (Array.isArray(target.host)) { - if (target.host.length === 0) { - throw new Error('No target hosts specified'); - } - - // Simple round-robin selection - const randomIndex = Math.floor(Math.random() * target.host.length); - return { - host: target.host[randomIndex], - port: this.resolvePort(target.port, incomingPort) - }; - } - - // Single host - return { - host: target.host, - port: this.resolvePort(target.port, incomingPort) - }; - } - - /** - * Resolves a port value, handling 'preserve' and function ports - * @param port The port value to resolve - * @param incomingPort Optional incoming port to use for 'preserve' mode - */ - protected resolvePort( - port: number | 'preserve' | ((ctx: any) => number), - incomingPort: number = 80 - ): number { - if (typeof port === 'function') { - try { - // Create a minimal context for the function that includes the incoming port - const ctx = { port: incomingPort }; - return port(ctx); - } catch (err) { - console.error('Error resolving port function:', err); - return incomingPort; // Fall back to incoming port - } - } else if (port === 'preserve') { - return incomingPort; // Use the actual incoming port for 'preserve' - } else { - return port; - } - } - - /** - * Redirect an HTTP request to HTTPS - * @param req The HTTP request - * @param res The HTTP response - */ - protected redirectToHttps(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void { - const host = req.headers.host || ''; - const path = req.url || '/'; - const redirectUrl = `https://${host}${path}`; - - res.writeHead(301, { - 'Location': redirectUrl, - 'Cache-Control': 'no-cache' - }); - res.end(`Redirecting to ${redirectUrl}`); - - this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, { - statusCode: 301, - headers: { 'Location': redirectUrl }, - size: 0 - }); - } - - /** - * Apply custom headers from configuration - * @param headers The original headers - * @param variables Variables to replace in the headers - * @returns The headers with custom values applied - */ - protected applyCustomHeaders( - headers: Record, - variables: Record - ): Record { - const customHeaders = this.config.advanced?.headers || {}; - const result = { ...headers }; - - // Apply custom headers with variable substitution - for (const [key, value] of Object.entries(customHeaders)) { - if (typeof value !== 'string') continue; - - let processedValue = value; - - // Replace variables in the header value - for (const [varName, varValue] of Object.entries(variables)) { - processedValue = processedValue.replace(`{${varName}}`, varValue); - } - - result[key] = processedValue; - } - - return result; - } - - /** - * Get the timeout for this connection from configuration - * @returns Timeout in milliseconds - */ - protected getTimeout(): number { - return this.config.advanced?.timeout || 60000; // Default: 60 seconds - } -} \ No newline at end of file diff --git a/ts/forwarding/handlers/http-handler.ts b/ts/forwarding/handlers/http-handler.ts deleted file mode 100644 index e8f533c..0000000 --- a/ts/forwarding/handlers/http-handler.ts +++ /dev/null @@ -1,163 +0,0 @@ -import * as plugins from '../../plugins.js'; -import { ForwardingHandler } from './base-handler.js'; -import type { IForwardConfig } from '../config/forwarding-types.js'; -import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; -import { setupSocketHandlers } from '../../core/utils/socket-utils.js'; - -/** - * Handler for HTTP-only forwarding - */ -export class HttpForwardingHandler extends ForwardingHandler { - /** - * Create a new HTTP forwarding handler - * @param config The forwarding configuration - */ - constructor(config: IForwardConfig) { - super(config); - - // Validate that this is an HTTP-only configuration - if (config.type !== 'http-only') { - throw new Error(`Invalid configuration type for HttpForwardingHandler: ${config.type}`); - } - } - - /** - * Initialize the handler - * HTTP handler doesn't need special initialization - */ - public async initialize(): Promise { - // Basic initialization from parent class - await super.initialize(); - } - - /** - * Handle a raw socket connection - * HTTP handler doesn't do much with raw sockets as it mainly processes - * parsed HTTP requests - */ - public handleConnection(socket: plugins.net.Socket): void { - // For HTTP, we mainly handle parsed requests, but we can still set up - // some basic connection tracking - const remoteAddress = socket.remoteAddress || 'unknown'; - const localPort = socket.localPort || 80; - - // Set up socket handlers with proper cleanup - const handleClose = (reason: string) => { - this.emit(ForwardingHandlerEvents.DISCONNECTED, { - remoteAddress, - reason - }); - }; - - // Use custom timeout handler that doesn't close the socket - setupSocketHandlers(socket, handleClose, () => { - // For HTTP, we can be more aggressive with timeouts since connections are shorter - // But still don't close immediately - let the connection finish naturally - console.warn(`HTTP socket timeout from ${remoteAddress}`); - }, 'http'); - - socket.on('error', (error) => { - this.emit(ForwardingHandlerEvents.ERROR, { - remoteAddress, - error: error.message - }); - }); - - this.emit(ForwardingHandlerEvents.CONNECTED, { - remoteAddress, - localPort - }); - } - - /** - * Handle an HTTP request - * @param req The HTTP request - * @param res The HTTP response - */ - public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void { - // Get the local port from the request (for 'preserve' port handling) - const localPort = req.socket.localPort || 80; - - // Get the target from configuration, passing the incoming port - const target = this.getTargetFromConfig(localPort); - - // Create a custom headers object with variables for substitution - const variables = { - clientIp: req.socket.remoteAddress || 'unknown' - }; - - // Prepare headers, merging with any custom headers from config - const headers = this.applyCustomHeaders(req.headers, variables); - - // Create the proxy request options - const options = { - hostname: target.host, - port: target.port, - path: req.url, - method: req.method, - headers - }; - - // Create the proxy request - const proxyReq = plugins.http.request(options, (proxyRes) => { - // Copy status code and headers from the proxied response - res.writeHead(proxyRes.statusCode || 500, proxyRes.headers); - - // Pipe the proxy response to the client response - proxyRes.pipe(res); - - // Track bytes for logging - let responseSize = 0; - proxyRes.on('data', (chunk) => { - responseSize += chunk.length; - }); - - proxyRes.on('end', () => { - this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, { - statusCode: proxyRes.statusCode, - headers: proxyRes.headers, - size: responseSize - }); - }); - }); - - // Handle errors in the proxy request - proxyReq.on('error', (error) => { - this.emit(ForwardingHandlerEvents.ERROR, { - remoteAddress: req.socket.remoteAddress, - error: `Proxy request error: ${error.message}` - }); - - // Send an error response if headers haven't been sent yet - if (!res.headersSent) { - res.writeHead(502, { 'Content-Type': 'text/plain' }); - res.end(`Error forwarding request: ${error.message}`); - } else { - // Just end the response if headers have already been sent - res.end(); - } - }); - - // Track request details for logging - let requestSize = 0; - req.on('data', (chunk) => { - requestSize += chunk.length; - }); - - // Log the request - this.emit(ForwardingHandlerEvents.HTTP_REQUEST, { - method: req.method, - url: req.url, - headers: req.headers, - remoteAddress: req.socket.remoteAddress, - target: `${target.host}:${target.port}` - }); - - // Pipe the client request to the proxy request - if (req.readable) { - req.pipe(proxyReq); - } else { - proxyReq.end(); - } - } -} \ No newline at end of file diff --git a/ts/forwarding/handlers/https-passthrough-handler.ts b/ts/forwarding/handlers/https-passthrough-handler.ts deleted file mode 100644 index 25f9dcf..0000000 --- a/ts/forwarding/handlers/https-passthrough-handler.ts +++ /dev/null @@ -1,185 +0,0 @@ -import * as plugins from '../../plugins.js'; -import { ForwardingHandler } from './base-handler.js'; -import type { IForwardConfig } from '../config/forwarding-types.js'; -import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; -import { createIndependentSocketHandlers, setupSocketHandlers, createSocketWithErrorHandler } from '../../core/utils/socket-utils.js'; - -/** - * Handler for HTTPS passthrough (SNI forwarding without termination) - */ -export class HttpsPassthroughHandler extends ForwardingHandler { - /** - * Create a new HTTPS passthrough handler - * @param config The forwarding configuration - */ - constructor(config: IForwardConfig) { - super(config); - - // Validate that this is an HTTPS passthrough configuration - if (config.type !== 'https-passthrough') { - throw new Error(`Invalid configuration type for HttpsPassthroughHandler: ${config.type}`); - } - } - - /** - * Initialize the handler - * HTTPS passthrough handler doesn't need special initialization - */ - public async initialize(): Promise { - // Basic initialization from parent class - await super.initialize(); - } - - /** - * Handle a TLS/SSL socket connection by forwarding it without termination - * @param clientSocket The incoming socket from the client - */ - public handleConnection(clientSocket: plugins.net.Socket): void { - // Get the target from configuration - const target = this.getTargetFromConfig(); - - // Log the connection - const remoteAddress = clientSocket.remoteAddress || 'unknown'; - const remotePort = clientSocket.remotePort || 0; - - this.emit(ForwardingHandlerEvents.CONNECTED, { - remoteAddress, - remotePort, - target: `${target.host}:${target.port}` - }); - - // Track data transfer for logging - let bytesSent = 0; - let bytesReceived = 0; - let serverSocket: plugins.net.Socket | null = null; - let cleanupClient: ((reason: string) => Promise) | null = null; - let cleanupServer: ((reason: string) => Promise) | null = null; - - // Create a connection to the target server with immediate error handling - serverSocket = createSocketWithErrorHandler({ - port: target.port, - host: target.host, - onError: async (error) => { - // Server connection failed - clean up client socket immediately - this.emit(ForwardingHandlerEvents.ERROR, { - error: error.message, - code: (error as any).code || 'UNKNOWN', - remoteAddress, - target: `${target.host}:${target.port}` - }); - - // Clean up the client socket since we can't forward - if (!clientSocket.destroyed) { - clientSocket.destroy(); - } - - this.emit(ForwardingHandlerEvents.DISCONNECTED, { - remoteAddress, - bytesSent: 0, - bytesReceived: 0, - reason: `server_connection_failed: ${error.message}` - }); - }, - onConnect: () => { - // Connection successful - set up forwarding handlers - const handlers = createIndependentSocketHandlers( - clientSocket, - serverSocket!, - (reason) => { - this.emit(ForwardingHandlerEvents.DISCONNECTED, { - remoteAddress, - bytesSent, - bytesReceived, - reason - }); - } - ); - - cleanupClient = handlers.cleanupClient; - cleanupServer = handlers.cleanupServer; - - // Setup handlers with custom timeout handling that doesn't close connections - const timeout = this.getTimeout(); - - setupSocketHandlers(clientSocket, cleanupClient, (socket) => { - // Just reset timeout, don't close - socket.setTimeout(timeout); - }, 'client'); - - setupSocketHandlers(serverSocket!, cleanupServer, (socket) => { - // Just reset timeout, don't close - socket.setTimeout(timeout); - }, 'server'); - - // Forward data from client to server - clientSocket.on('data', (data) => { - bytesSent += data.length; - - // Check if server socket is writable - if (serverSocket && serverSocket.writable) { - const flushed = serverSocket.write(data); - - // Handle backpressure - if (!flushed) { - clientSocket.pause(); - serverSocket.once('drain', () => { - clientSocket.resume(); - }); - } - } - - this.emit(ForwardingHandlerEvents.DATA_FORWARDED, { - direction: 'outbound', - bytes: data.length, - total: bytesSent - }); - }); - - // Forward data from server to client - serverSocket!.on('data', (data) => { - bytesReceived += data.length; - - // Check if client socket is writable - if (clientSocket.writable) { - const flushed = clientSocket.write(data); - - // Handle backpressure - if (!flushed) { - serverSocket!.pause(); - clientSocket.once('drain', () => { - serverSocket!.resume(); - }); - } - } - - this.emit(ForwardingHandlerEvents.DATA_FORWARDED, { - direction: 'inbound', - bytes: data.length, - total: bytesReceived - }); - }); - - // Set initial timeouts - they will be reset on each timeout event - clientSocket.setTimeout(timeout); - serverSocket!.setTimeout(timeout); - } - }); - } - - /** - * Handle an HTTP request - HTTPS passthrough doesn't support HTTP - * @param req The HTTP request - * @param res The HTTP response - */ - public handleHttpRequest(_req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void { - // HTTPS passthrough doesn't support HTTP requests - res.writeHead(404, { 'Content-Type': 'text/plain' }); - res.end('HTTP not supported for this domain'); - - this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, { - statusCode: 404, - headers: { 'Content-Type': 'text/plain' }, - size: 'HTTP not supported for this domain'.length - }); - } -} \ No newline at end of file diff --git a/ts/forwarding/handlers/https-terminate-to-http-handler.ts b/ts/forwarding/handlers/https-terminate-to-http-handler.ts deleted file mode 100644 index 4b8b180..0000000 --- a/ts/forwarding/handlers/https-terminate-to-http-handler.ts +++ /dev/null @@ -1,312 +0,0 @@ -import * as plugins from '../../plugins.js'; -import { ForwardingHandler } from './base-handler.js'; -import type { IForwardConfig } from '../config/forwarding-types.js'; -import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; -import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js'; - -/** - * Handler for HTTPS termination with HTTP backend - */ -export class HttpsTerminateToHttpHandler extends ForwardingHandler { - private tlsServer: plugins.tls.Server | null = null; - private secureContext: plugins.tls.SecureContext | null = null; - - /** - * Create a new HTTPS termination with HTTP backend handler - * @param config The forwarding configuration - */ - constructor(config: IForwardConfig) { - super(config); - - // Validate that this is an HTTPS terminate to HTTP configuration - if (config.type !== 'https-terminate-to-http') { - throw new Error(`Invalid configuration type for HttpsTerminateToHttpHandler: ${config.type}`); - } - } - - /** - * Initialize the handler, setting up TLS context - */ - public async initialize(): Promise { - // We need to load or create TLS certificates - if (this.config.https?.customCert) { - // Use custom certificate from configuration - this.secureContext = plugins.tls.createSecureContext({ - key: this.config.https.customCert.key, - cert: this.config.https.customCert.cert - }); - - this.emit(ForwardingHandlerEvents.CERTIFICATE_LOADED, { - source: 'config', - domain: this.config.target.host - }); - } else if (this.config.acme?.enabled) { - // Request certificate through ACME if needed - this.emit(ForwardingHandlerEvents.CERTIFICATE_NEEDED, { - domain: Array.isArray(this.config.target.host) - ? this.config.target.host[0] - : this.config.target.host, - useProduction: this.config.acme.production || false - }); - - // In a real implementation, we would wait for the certificate to be issued - // For now, we'll use a dummy context - this.secureContext = plugins.tls.createSecureContext({ - key: '-----BEGIN PRIVATE KEY-----\nDummy key\n-----END PRIVATE KEY-----', - cert: '-----BEGIN CERTIFICATE-----\nDummy cert\n-----END CERTIFICATE-----' - }); - } else { - throw new Error('HTTPS termination requires either a custom certificate or ACME enabled'); - } - } - - /** - * Set the secure context for TLS termination - * Called when a certificate is available - * @param context The secure context - */ - public setSecureContext(context: plugins.tls.SecureContext): void { - this.secureContext = context; - } - - /** - * Handle a TLS/SSL socket connection by terminating TLS and forwarding to HTTP backend - * @param clientSocket The incoming socket from the client - */ - public handleConnection(clientSocket: plugins.net.Socket): void { - // Make sure we have a secure context - if (!this.secureContext) { - clientSocket.destroy(new Error('TLS secure context not initialized')); - return; - } - - const remoteAddress = clientSocket.remoteAddress || 'unknown'; - const remotePort = clientSocket.remotePort || 0; - - // Create a TLS socket using our secure context - const tlsSocket = new plugins.tls.TLSSocket(clientSocket, { - secureContext: this.secureContext, - isServer: true, - server: this.tlsServer || undefined - }); - - this.emit(ForwardingHandlerEvents.CONNECTED, { - remoteAddress, - remotePort, - tls: true - }); - - // Variables to track connections - let backendSocket: plugins.net.Socket | null = null; - let dataBuffer = Buffer.alloc(0); - let connectionEstablished = false; - let forwardingSetup = false; - - // Set up initial error handling for TLS socket - const tlsCleanupHandler = (reason: string) => { - if (!forwardingSetup) { - // If forwarding not set up yet, emit disconnected and cleanup - this.emit(ForwardingHandlerEvents.DISCONNECTED, { - remoteAddress, - reason - }); - dataBuffer = Buffer.alloc(0); - connectionEstablished = false; - - if (!tlsSocket.destroyed) { - tlsSocket.destroy(); - } - if (backendSocket && !backendSocket.destroyed) { - backendSocket.destroy(); - } - } - // If forwarding is setup, setupBidirectionalForwarding will handle cleanup - }; - - setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls'); - - // Set timeout - const timeout = this.getTimeout(); - tlsSocket.setTimeout(timeout); - - tlsSocket.on('timeout', () => { - this.emit(ForwardingHandlerEvents.ERROR, { - remoteAddress, - error: 'TLS connection timeout' - }); - tlsCleanupHandler('timeout'); - }); - - // Handle TLS data - tlsSocket.on('data', (data) => { - // If backend connection already established, just forward the data - if (connectionEstablished && backendSocket && !backendSocket.destroyed) { - backendSocket.write(data); - return; - } - - // Append to buffer - dataBuffer = Buffer.concat([dataBuffer, data]); - - // Very basic HTTP parsing - in a real implementation, use http-parser - if (dataBuffer.includes(Buffer.from('\r\n\r\n')) && !connectionEstablished) { - const target = this.getTargetFromConfig(); - - // Create backend connection with immediate error handling - backendSocket = createSocketWithErrorHandler({ - port: target.port, - host: target.host, - onError: (error) => { - this.emit(ForwardingHandlerEvents.ERROR, { - error: error.message, - code: (error as any).code || 'UNKNOWN', - remoteAddress, - target: `${target.host}:${target.port}` - }); - - // Clean up the TLS socket since we can't forward - if (!tlsSocket.destroyed) { - tlsSocket.destroy(); - } - - this.emit(ForwardingHandlerEvents.DISCONNECTED, { - remoteAddress, - reason: `backend_connection_failed: ${error.message}` - }); - }, - onConnect: () => { - connectionEstablished = true; - - // Send buffered data - if (dataBuffer.length > 0) { - backendSocket!.write(dataBuffer); - dataBuffer = Buffer.alloc(0); - } - - // Now set up bidirectional forwarding with proper cleanup - forwardingSetup = true; - setupBidirectionalForwarding(tlsSocket, backendSocket!, { - onCleanup: (reason) => { - this.emit(ForwardingHandlerEvents.DISCONNECTED, { - remoteAddress, - reason - }); - dataBuffer = Buffer.alloc(0); - connectionEstablished = false; - forwardingSetup = false; - }, - enableHalfOpen: false // Close both when one closes - }); - } - }); - - // Additional error logging for backend socket - backendSocket.on('error', (error) => { - if (!connectionEstablished) { - // Connection failed during setup - this.emit(ForwardingHandlerEvents.ERROR, { - remoteAddress, - error: `Target connection error: ${error.message}` - }); - } - // If connected, setupBidirectionalForwarding handles cleanup - }); - } - }); - } - - /** - * Handle an HTTP request by forwarding to the HTTP backend - * @param req The HTTP request - * @param res The HTTP response - */ - public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void { - // Check if we should redirect to HTTPS - if (this.config.http?.redirectToHttps) { - this.redirectToHttps(req, res); - return; - } - - // Get the target from configuration - const target = this.getTargetFromConfig(); - - // Create custom headers with variable substitution - const variables = { - clientIp: req.socket.remoteAddress || 'unknown' - }; - - // Prepare headers, merging with any custom headers from config - const headers = this.applyCustomHeaders(req.headers, variables); - - // Create the proxy request options - const options = { - hostname: target.host, - port: target.port, - path: req.url, - method: req.method, - headers - }; - - // Create the proxy request - const proxyReq = plugins.http.request(options, (proxyRes) => { - // Copy status code and headers from the proxied response - res.writeHead(proxyRes.statusCode || 500, proxyRes.headers); - - // Pipe the proxy response to the client response - proxyRes.pipe(res); - - // Track response size for logging - let responseSize = 0; - proxyRes.on('data', (chunk) => { - responseSize += chunk.length; - }); - - proxyRes.on('end', () => { - this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, { - statusCode: proxyRes.statusCode, - headers: proxyRes.headers, - size: responseSize - }); - }); - }); - - // Handle errors in the proxy request - proxyReq.on('error', (error) => { - this.emit(ForwardingHandlerEvents.ERROR, { - remoteAddress: req.socket.remoteAddress, - error: `Proxy request error: ${error.message}` - }); - - // Send an error response if headers haven't been sent yet - if (!res.headersSent) { - res.writeHead(502, { 'Content-Type': 'text/plain' }); - res.end(`Error forwarding request: ${error.message}`); - } else { - // Just end the response if headers have already been sent - res.end(); - } - }); - - // Track request details for logging - let requestSize = 0; - req.on('data', (chunk) => { - requestSize += chunk.length; - }); - - // Log the request - this.emit(ForwardingHandlerEvents.HTTP_REQUEST, { - method: req.method, - url: req.url, - headers: req.headers, - remoteAddress: req.socket.remoteAddress, - target: `${target.host}:${target.port}` - }); - - // Pipe the client request to the proxy request - if (req.readable) { - req.pipe(proxyReq); - } else { - proxyReq.end(); - } - } -} \ No newline at end of file diff --git a/ts/forwarding/handlers/https-terminate-to-https-handler.ts b/ts/forwarding/handlers/https-terminate-to-https-handler.ts deleted file mode 100644 index 1bdbe99..0000000 --- a/ts/forwarding/handlers/https-terminate-to-https-handler.ts +++ /dev/null @@ -1,297 +0,0 @@ -import * as plugins from '../../plugins.js'; -import { ForwardingHandler } from './base-handler.js'; -import type { IForwardConfig } from '../config/forwarding-types.js'; -import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; -import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js'; - -/** - * Handler for HTTPS termination with HTTPS backend - */ -export class HttpsTerminateToHttpsHandler extends ForwardingHandler { - private secureContext: plugins.tls.SecureContext | null = null; - - /** - * Create a new HTTPS termination with HTTPS backend handler - * @param config The forwarding configuration - */ - constructor(config: IForwardConfig) { - super(config); - - // Validate that this is an HTTPS terminate to HTTPS configuration - if (config.type !== 'https-terminate-to-https') { - throw new Error(`Invalid configuration type for HttpsTerminateToHttpsHandler: ${config.type}`); - } - } - - /** - * Initialize the handler, setting up TLS context - */ - public async initialize(): Promise { - // We need to load or create TLS certificates for termination - if (this.config.https?.customCert) { - // Use custom certificate from configuration - this.secureContext = plugins.tls.createSecureContext({ - key: this.config.https.customCert.key, - cert: this.config.https.customCert.cert - }); - - this.emit(ForwardingHandlerEvents.CERTIFICATE_LOADED, { - source: 'config', - domain: this.config.target.host - }); - } else if (this.config.acme?.enabled) { - // Request certificate through ACME if needed - this.emit(ForwardingHandlerEvents.CERTIFICATE_NEEDED, { - domain: Array.isArray(this.config.target.host) - ? this.config.target.host[0] - : this.config.target.host, - useProduction: this.config.acme.production || false - }); - - // In a real implementation, we would wait for the certificate to be issued - // For now, we'll use a dummy context - this.secureContext = plugins.tls.createSecureContext({ - key: '-----BEGIN PRIVATE KEY-----\nDummy key\n-----END PRIVATE KEY-----', - cert: '-----BEGIN CERTIFICATE-----\nDummy cert\n-----END CERTIFICATE-----' - }); - } else { - throw new Error('HTTPS termination requires either a custom certificate or ACME enabled'); - } - } - - /** - * Set the secure context for TLS termination - * Called when a certificate is available - * @param context The secure context - */ - public setSecureContext(context: plugins.tls.SecureContext): void { - this.secureContext = context; - } - - /** - * Handle a TLS/SSL socket connection by terminating TLS and creating a new TLS connection to backend - * @param clientSocket The incoming socket from the client - */ - public handleConnection(clientSocket: plugins.net.Socket): void { - // Make sure we have a secure context - if (!this.secureContext) { - clientSocket.destroy(new Error('TLS secure context not initialized')); - return; - } - - const remoteAddress = clientSocket.remoteAddress || 'unknown'; - const remotePort = clientSocket.remotePort || 0; - - // Create a TLS socket using our secure context - const tlsSocket = new plugins.tls.TLSSocket(clientSocket, { - secureContext: this.secureContext, - isServer: true - }); - - this.emit(ForwardingHandlerEvents.CONNECTED, { - remoteAddress, - remotePort, - tls: true - }); - - // Variable to track backend socket - let backendSocket: plugins.tls.TLSSocket | null = null; - let isConnectedToBackend = false; - - // Set up initial error handling for TLS socket - const tlsCleanupHandler = (reason: string) => { - if (!isConnectedToBackend) { - // If backend not connected yet, just emit disconnected event - this.emit(ForwardingHandlerEvents.DISCONNECTED, { - remoteAddress, - reason - }); - - // Cleanup TLS socket if needed - if (!tlsSocket.destroyed) { - tlsSocket.destroy(); - } - } - // If connected to backend, setupBidirectionalForwarding will handle cleanup - }; - - setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls'); - - // Set timeout - const timeout = this.getTimeout(); - tlsSocket.setTimeout(timeout); - - tlsSocket.on('timeout', () => { - this.emit(ForwardingHandlerEvents.ERROR, { - remoteAddress, - error: 'TLS connection timeout' - }); - tlsCleanupHandler('timeout'); - }); - - // Get the target from configuration - const target = this.getTargetFromConfig(); - - // Set up the connection to the HTTPS backend - const connectToBackend = () => { - backendSocket = plugins.tls.connect({ - host: target.host, - port: target.port, - // In a real implementation, we would configure TLS options - rejectUnauthorized: false // For testing only, never use in production - }, () => { - isConnectedToBackend = true; - - this.emit(ForwardingHandlerEvents.DATA_FORWARDED, { - direction: 'outbound', - target: `${target.host}:${target.port}`, - tls: true - }); - - // Set up bidirectional forwarding with proper cleanup - setupBidirectionalForwarding(tlsSocket, backendSocket!, { - onCleanup: (reason) => { - this.emit(ForwardingHandlerEvents.DISCONNECTED, { - remoteAddress, - reason - }); - }, - enableHalfOpen: false // Close both when one closes - }); - - // Set timeout for backend socket - backendSocket!.setTimeout(timeout); - - backendSocket!.on('timeout', () => { - this.emit(ForwardingHandlerEvents.ERROR, { - remoteAddress, - error: 'Backend connection timeout' - }); - // Let setupBidirectionalForwarding handle the cleanup - }); - }); - - // Handle backend connection errors - backendSocket.on('error', (error) => { - this.emit(ForwardingHandlerEvents.ERROR, { - remoteAddress, - error: `Backend connection error: ${error.message}` - }); - - if (!isConnectedToBackend) { - // Connection failed, clean up TLS socket - if (!tlsSocket.destroyed) { - tlsSocket.destroy(); - } - this.emit(ForwardingHandlerEvents.DISCONNECTED, { - remoteAddress, - reason: `backend_connection_failed: ${error.message}` - }); - } - // If connected, let setupBidirectionalForwarding handle cleanup - }); - }; - - // Wait for the TLS handshake to complete before connecting to backend - tlsSocket.on('secure', () => { - connectToBackend(); - }); - } - - /** - * Handle an HTTP request by forwarding to the HTTPS backend - * @param req The HTTP request - * @param res The HTTP response - */ - public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void { - // Check if we should redirect to HTTPS - if (this.config.http?.redirectToHttps) { - this.redirectToHttps(req, res); - return; - } - - // Get the target from configuration - const target = this.getTargetFromConfig(); - - // Create custom headers with variable substitution - const variables = { - clientIp: req.socket.remoteAddress || 'unknown' - }; - - // Prepare headers, merging with any custom headers from config - const headers = this.applyCustomHeaders(req.headers, variables); - - // Create the proxy request options - const options = { - hostname: target.host, - port: target.port, - path: req.url, - method: req.method, - headers, - // In a real implementation, we would configure TLS options - rejectUnauthorized: false // For testing only, never use in production - }; - - // Create the proxy request using HTTPS - const proxyReq = plugins.https.request(options, (proxyRes) => { - // Copy status code and headers from the proxied response - res.writeHead(proxyRes.statusCode || 500, proxyRes.headers); - - // Pipe the proxy response to the client response - proxyRes.pipe(res); - - // Track response size for logging - let responseSize = 0; - proxyRes.on('data', (chunk) => { - responseSize += chunk.length; - }); - - proxyRes.on('end', () => { - this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, { - statusCode: proxyRes.statusCode, - headers: proxyRes.headers, - size: responseSize - }); - }); - }); - - // Handle errors in the proxy request - proxyReq.on('error', (error) => { - this.emit(ForwardingHandlerEvents.ERROR, { - remoteAddress: req.socket.remoteAddress, - error: `Proxy request error: ${error.message}` - }); - - // Send an error response if headers haven't been sent yet - if (!res.headersSent) { - res.writeHead(502, { 'Content-Type': 'text/plain' }); - res.end(`Error forwarding request: ${error.message}`); - } else { - // Just end the response if headers have already been sent - res.end(); - } - }); - - // Track request details for logging - let requestSize = 0; - req.on('data', (chunk) => { - requestSize += chunk.length; - }); - - // Log the request - this.emit(ForwardingHandlerEvents.HTTP_REQUEST, { - method: req.method, - url: req.url, - headers: req.headers, - remoteAddress: req.socket.remoteAddress, - target: `${target.host}:${target.port}` - }); - - // Pipe the client request to the proxy request - if (req.readable) { - req.pipe(proxyReq); - } else { - proxyReq.end(); - } - } -} \ No newline at end of file diff --git a/ts/forwarding/handlers/index.ts b/ts/forwarding/handlers/index.ts deleted file mode 100644 index 933ea8d..0000000 --- a/ts/forwarding/handlers/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Forwarding handler implementations - */ - -export { ForwardingHandler } from './base-handler.js'; -export { HttpForwardingHandler } from './http-handler.js'; -export { HttpsPassthroughHandler } from './https-passthrough-handler.js'; -export { HttpsTerminateToHttpHandler } from './https-terminate-to-http-handler.js'; -export { HttpsTerminateToHttpsHandler } from './https-terminate-to-https-handler.js'; \ No newline at end of file diff --git a/ts/forwarding/index.ts b/ts/forwarding/index.ts deleted file mode 100644 index d2823e6..0000000 --- a/ts/forwarding/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Forwarding system module - * Provides a flexible and type-safe way to configure and manage various forwarding strategies - */ - -// Export handlers -export { ForwardingHandler } from './handlers/base-handler.js'; -export * from './handlers/http-handler.js'; -export * from './handlers/https-passthrough-handler.js'; -export * from './handlers/https-terminate-to-http-handler.js'; -export * from './handlers/https-terminate-to-https-handler.js'; - -// Export factory -export * from './factory/forwarding-factory.js'; - -// Export types - these include TForwardingType and IForwardConfig -export type { - TForwardingType, - IForwardConfig, - IForwardingHandler -} from './config/forwarding-types.js'; - -export { - ForwardingHandlerEvents -} from './config/forwarding-types.js'; - -// Export route helpers directly from route-patterns -export { - createHttpRoute, - createHttpsTerminateRoute, - createHttpsPassthroughRoute, - createHttpToHttpsRedirect, - createCompleteHttpsServer, - createLoadBalancerRoute -} from '../proxies/smart-proxy/utils/route-patterns.js'; \ No newline at end of file diff --git a/ts/index.ts b/ts/index.ts index 1c9bb40..fa8d291 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -32,7 +32,6 @@ export * from './core/models/common-types.js'; export type { IAcmeOptions } from './proxies/smart-proxy/models/interfaces.js'; // Modular exports for new architecture -export * as forwarding from './forwarding/index.js'; // Certificate module has been removed - use SmartCertManager instead export * as tls from './tls/index.js'; export * as routing from './routing/index.js'; \ No newline at end of file diff --git a/ts/proxies/smart-proxy/models/interfaces.ts b/ts/proxies/smart-proxy/models/interfaces.ts index e98a2e8..472dcdc 100644 --- a/ts/proxies/smart-proxy/models/interfaces.ts +++ b/ts/proxies/smart-proxy/models/interfaces.ts @@ -16,7 +16,6 @@ export interface IAcmeOptions { routeForwards?: any[]; } import type { IRouteConfig } from './route-types.js'; -import type { TForwardingType } from '../../../forwarding/config/forwarding-types.js'; /** * Provision object for static or HTTP-01 certificate diff --git a/ts/proxies/smart-proxy/models/route-types.ts b/ts/proxies/smart-proxy/models/route-types.ts index e2a1d0c..791617c 100644 --- a/ts/proxies/smart-proxy/models/route-types.ts +++ b/ts/proxies/smart-proxy/models/route-types.ts @@ -1,6 +1,5 @@ import * as plugins from '../../../plugins.js'; // Certificate types removed - use local definition -import type { TForwardingType } from '../../../forwarding/config/forwarding-types.js'; import type { PortRange } from '../../../proxies/nftables-proxy/models/interfaces.js'; import type { IRouteContext } from '../../../core/models/route-context.js'; diff --git a/ts/proxies/smart-proxy/utils/index.ts b/ts/proxies/smart-proxy/utils/index.ts index d02885d..88c1ef8 100644 --- a/ts/proxies/smart-proxy/utils/index.ts +++ b/ts/proxies/smart-proxy/utils/index.ts @@ -14,23 +14,12 @@ export * from './route-validators.js'; // Export route utilities for route operations export * from './route-utils.js'; -// Export route patterns with renamed exports to avoid conflicts -import { - createWebSocketRoute as createWebSocketPatternRoute, - createLoadBalancerRoute as createLoadBalancerPatternRoute, - createApiGatewayRoute, - addRateLimiting, - addBasicAuth, - addJwtAuth -} from './route-patterns.js'; - +// Export additional functions from route-helpers that weren't already exported export { - createWebSocketPatternRoute, - createLoadBalancerPatternRoute, createApiGatewayRoute, addRateLimiting, addBasicAuth, addJwtAuth -}; +} from './route-helpers.js'; // Migration utilities have been removed as they are no longer needed \ No newline at end of file diff --git a/ts/proxies/smart-proxy/utils/route-helpers.ts b/ts/proxies/smart-proxy/utils/route-helpers.ts index c8e124c..b416039 100644 --- a/ts/proxies/smart-proxy/utils/route-helpers.ts +++ b/ts/proxies/smart-proxy/utils/route-helpers.ts @@ -20,6 +20,7 @@ import * as plugins from '../../../plugins.js'; import type { IRouteConfig, IRouteMatch, IRouteAction, IRouteTarget, TPortRange, IRouteContext } from '../models/route-types.js'; +import { mergeRouteConfigs } from './route-utils.js'; /** * Create an HTTP-only route configuration @@ -211,26 +212,62 @@ export function createCompleteHttpsServer( /** * Create a load balancer route (round-robin between multiple backend hosts) * @param domains Domain(s) to match - * @param hosts Array of backend hosts to load balance between - * @param port Backend port - * @param options Additional route options + * @param backendsOrHosts Array of backend servers OR array of host strings (legacy) + * @param portOrOptions Port number (legacy) OR options object + * @param options Additional route options (legacy) * @returns Route configuration object */ export function createLoadBalancerRoute( domains: string | string[], - hosts: string[], - port: number, - options: { + backendsOrHosts: Array<{ host: string; port: number }> | string[], + portOrOptions?: number | { + tls?: { + mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt'; + certificate?: 'auto' | { key: string; cert: string }; + }; + useTls?: boolean; + certificate?: 'auto' | { key: string; cert: string }; + algorithm?: 'round-robin' | 'least-connections' | 'ip-hash'; + healthCheck?: { + path: string; + interval: number; + timeout: number; + unhealthyThreshold: number; + healthyThreshold: number; + }; + [key: string]: any; + }, + options?: { tls?: { mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt'; certificate?: 'auto' | { key: string; cert: string }; }; [key: string]: any; - } = {} + } ): IRouteConfig { + // Handle legacy signature: (domains, hosts[], port, options) + let backends: Array<{ host: string; port: number }>; + let finalOptions: any; + + if (Array.isArray(backendsOrHosts) && backendsOrHosts.length > 0 && typeof backendsOrHosts[0] === 'string') { + // Legacy signature + const hosts = backendsOrHosts as string[]; + const port = portOrOptions as number; + backends = hosts.map(host => ({ host, port })); + finalOptions = options || {}; + } else { + // New signature + backends = backendsOrHosts as Array<{ host: string; port: number }>; + finalOptions = (portOrOptions as any) || {}; + } + + // Extract hosts and ensure all backends use the same port + const port = backends[0].port; + const hosts = backends.map(backend => backend.host); + // Create route match const match: IRouteMatch = { - ports: options.match?.ports || (options.tls ? 443 : 80), + ports: finalOptions.match?.ports || (finalOptions.tls || finalOptions.useTls ? 443 : 80), domains }; @@ -247,10 +284,18 @@ export function createLoadBalancerRoute( }; // Add TLS configuration if provided - if (options.tls) { + if (finalOptions.tls || finalOptions.useTls) { action.tls = { - mode: options.tls.mode, - certificate: options.tls.certificate || 'auto' + mode: finalOptions.tls?.mode || 'terminate', + certificate: finalOptions.tls?.certificate || finalOptions.certificate || 'auto' + }; + } + + // Add load balancing options + if (finalOptions.algorithm || finalOptions.healthCheck) { + action.loadBalancing = { + algorithm: finalOptions.algorithm || 'round-robin', + healthCheck: finalOptions.healthCheck }; } @@ -258,8 +303,8 @@ export function createLoadBalancerRoute( return { match, action, - name: options.name || `Load Balancer for ${Array.isArray(domains) ? domains.join(', ') : domains}`, - ...options + name: finalOptions.name || `Load Balancer for ${Array.isArray(domains) ? domains.join(', ') : domains}`, + ...finalOptions }; } @@ -339,16 +384,26 @@ export function createApiRoute( /** * Create a WebSocket route configuration * @param domains Domain(s) to match - * @param wsPath WebSocket path (e.g., "/ws") - * @param target Target WebSocket server host and port - * @param options Additional route options + * @param targetOrPath Target server OR WebSocket path (legacy) + * @param targetOrOptions Target server (legacy) OR options + * @param options Additional route options (legacy) * @returns Route configuration object */ export function createWebSocketRoute( domains: string | string[], - wsPath: string, - target: { host: string | string[]; port: number }, - options: { + targetOrPath: { host: string | string[]; port: number } | string, + targetOrOptions?: { host: string | string[]; port: number } | { + useTls?: boolean; + certificate?: 'auto' | { key: string; cert: string }; + path?: string; + httpPort?: number | number[]; + httpsPort?: number | number[]; + pingInterval?: number; + pingTimeout?: number; + name?: string; + [key: string]: any; + }, + options?: { useTls?: boolean; certificate?: 'auto' | { key: string; cert: string }; httpPort?: number | number[]; @@ -357,16 +412,33 @@ export function createWebSocketRoute( pingTimeout?: number; name?: string; [key: string]: any; - } = {} + } ): IRouteConfig { + // Handle different signatures + let target: { host: string | string[]; port: number }; + let wsPath: string; + let finalOptions: any; + + if (typeof targetOrPath === 'string') { + // Legacy signature: (domains, path, target, options) + wsPath = targetOrPath; + target = targetOrOptions as { host: string | string[]; port: number }; + finalOptions = options || {}; + } else { + // New signature: (domains, target, options) + target = targetOrPath; + finalOptions = (targetOrOptions as any) || {}; + wsPath = finalOptions.path || '/ws'; + } + // Normalize WebSocket path const normalizedPath = wsPath.startsWith('/') ? wsPath : `/${wsPath}`; // Create route match const match: IRouteMatch = { - ports: options.useTls - ? (options.httpsPort || 443) - : (options.httpPort || 80), + ports: finalOptions.useTls + ? (finalOptions.httpsPort || 443) + : (finalOptions.httpPort || 80), domains, path: normalizedPath }; @@ -377,16 +449,16 @@ export function createWebSocketRoute( targets: [target], websocket: { enabled: true, - pingInterval: options.pingInterval || 30000, // 30 seconds - pingTimeout: options.pingTimeout || 5000 // 5 seconds + pingInterval: finalOptions.pingInterval || 30000, // 30 seconds + pingTimeout: finalOptions.pingTimeout || 5000 // 5 seconds } }; // Add TLS configuration if using HTTPS - if (options.useTls) { + if (finalOptions.useTls) { action.tls = { mode: 'terminate', - certificate: options.certificate || 'auto' + certificate: finalOptions.certificate || 'auto' }; } @@ -394,9 +466,9 @@ export function createWebSocketRoute( return { match, action, - name: options.name || `WebSocket Route ${normalizedPath} for ${Array.isArray(domains) ? domains.join(', ') : domains}`, - priority: options.priority || 100, // Higher priority for WebSocket routes - ...options + name: finalOptions.name || `WebSocket Route ${normalizedPath} for ${Array.isArray(domains) ? domains.join(', ') : domains}`, + priority: finalOptions.priority || 100, // Higher priority for WebSocket routes + ...finalOptions }; } @@ -1030,3 +1102,152 @@ export const SocketHandlers = { }); } }; + +/** + * Create an API Gateway route pattern + * @param domains Domain(s) to match + * @param apiBasePath Base path for API endpoints (e.g., '/api') + * @param target Target host and port + * @param options Additional route options + * @returns API route configuration + */ +export function createApiGatewayRoute( + domains: string | string[], + apiBasePath: string, + target: { host: string | string[]; port: number }, + options: { + useTls?: boolean; + certificate?: 'auto' | { key: string; cert: string }; + addCorsHeaders?: boolean; + [key: string]: any; + } = {} +): IRouteConfig { + // Normalize apiBasePath to ensure it starts with / and doesn't end with / + const normalizedPath = apiBasePath.startsWith('/') + ? apiBasePath + : `/${apiBasePath}`; + + // Add wildcard to path to match all API endpoints + const apiPath = normalizedPath.endsWith('/') + ? `${normalizedPath}*` + : `${normalizedPath}/*`; + + // Create base route + const baseRoute = options.useTls + ? createHttpsTerminateRoute(domains, target, { + certificate: options.certificate || 'auto' + }) + : createHttpRoute(domains, target); + + // Add API-specific configurations + const apiRoute: Partial = { + match: { + ...baseRoute.match, + path: apiPath + }, + name: options.name || `API Gateway: ${apiPath} -> ${Array.isArray(target.host) ? target.host.join(', ') : target.host}:${target.port}`, + priority: options.priority || 100 // Higher priority for specific path matching + }; + + // Add CORS headers if requested + if (options.addCorsHeaders) { + apiRoute.headers = { + response: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', + 'Access-Control-Max-Age': '86400' + } + }; + } + + return mergeRouteConfigs(baseRoute, apiRoute); +} + +/** + * Create a rate limiting route pattern + * @param baseRoute Base route to add rate limiting to + * @param rateLimit Rate limiting configuration + * @returns Route with rate limiting + */ +export function addRateLimiting( + baseRoute: IRouteConfig, + rateLimit: { + maxRequests: number; + window: number; // Time window in seconds + keyBy?: 'ip' | 'path' | 'header'; + headerName?: string; // Required if keyBy is 'header' + errorMessage?: string; + } +): IRouteConfig { + return mergeRouteConfigs(baseRoute, { + security: { + rateLimit: { + enabled: true, + maxRequests: rateLimit.maxRequests, + window: rateLimit.window, + keyBy: rateLimit.keyBy || 'ip', + headerName: rateLimit.headerName, + errorMessage: rateLimit.errorMessage || 'Rate limit exceeded. Please try again later.' + } + } + }); +} + +/** + * Create a basic authentication route pattern + * @param baseRoute Base route to add authentication to + * @param auth Authentication configuration + * @returns Route with basic authentication + */ +export function addBasicAuth( + baseRoute: IRouteConfig, + auth: { + users: Array<{ username: string; password: string }>; + realm?: string; + excludePaths?: string[]; + } +): IRouteConfig { + return mergeRouteConfigs(baseRoute, { + security: { + basicAuth: { + enabled: true, + users: auth.users, + realm: auth.realm || 'Restricted Area', + excludePaths: auth.excludePaths || [] + } + } + }); +} + +/** + * Create a JWT authentication route pattern + * @param baseRoute Base route to add JWT authentication to + * @param jwt JWT authentication configuration + * @returns Route with JWT authentication + */ +export function addJwtAuth( + baseRoute: IRouteConfig, + jwt: { + secret: string; + algorithm?: string; + issuer?: string; + audience?: string; + expiresIn?: number; // Time in seconds + excludePaths?: string[]; + } +): IRouteConfig { + return mergeRouteConfigs(baseRoute, { + security: { + jwtAuth: { + enabled: true, + secret: jwt.secret, + algorithm: jwt.algorithm || 'HS256', + issuer: jwt.issuer, + audience: jwt.audience, + expiresIn: jwt.expiresIn, + excludePaths: jwt.excludePaths || [] + } + } + }); +} diff --git a/ts/proxies/smart-proxy/utils/route-patterns.ts b/ts/proxies/smart-proxy/utils/route-patterns.ts deleted file mode 100644 index 40cc0c7..0000000 --- a/ts/proxies/smart-proxy/utils/route-patterns.ts +++ /dev/null @@ -1,403 +0,0 @@ -/** - * Route Patterns - * - * This file provides pre-defined route patterns for common use cases. - * These patterns can be used as templates for creating route configurations. - */ - -import type { IRouteConfig, IRouteMatch, IRouteAction, IRouteTarget } from '../models/route-types.js'; -import { mergeRouteConfigs } from './route-utils.js'; -import { SocketHandlers } from './route-helpers.js'; - -/** - * Create a basic HTTP route configuration - */ -export function createHttpRoute( - domains: string | string[], - target: { host: string | string[]; port: number | 'preserve' | ((ctx: any) => number) }, - options: Partial = {} -): IRouteConfig { - const route: IRouteConfig = { - match: { - domains, - ports: 80 - }, - action: { - type: 'forward', - targets: [{ - host: target.host, - port: target.port - }] - }, - name: options.name || `HTTP: ${Array.isArray(domains) ? domains.join(', ') : domains}` - }; - - return mergeRouteConfigs(route, options); -} - -/** - * Create an HTTPS route with TLS termination - */ -export function createHttpsTerminateRoute( - domains: string | string[], - target: { host: string | string[]; port: number | 'preserve' | ((ctx: any) => number) }, - options: Partial & { - certificate?: 'auto' | { key: string; cert: string }; - reencrypt?: boolean; - } = {} -): IRouteConfig { - const route: IRouteConfig = { - match: { - domains, - ports: 443 - }, - action: { - type: 'forward', - targets: [{ - host: target.host, - port: target.port - }], - tls: { - mode: options.reencrypt ? 'terminate-and-reencrypt' : 'terminate', - certificate: options.certificate || 'auto' - } - }, - name: options.name || `HTTPS (terminate): ${Array.isArray(domains) ? domains.join(', ') : domains}` - }; - - return mergeRouteConfigs(route, options); -} - -/** - * Create an HTTPS route with TLS passthrough - */ -export function createHttpsPassthroughRoute( - domains: string | string[], - target: { host: string | string[]; port: number | 'preserve' | ((ctx: any) => number) }, - options: Partial = {} -): IRouteConfig { - const route: IRouteConfig = { - match: { - domains, - ports: 443 - }, - action: { - type: 'forward', - targets: [{ - host: target.host, - port: target.port - }], - tls: { - mode: 'passthrough' - } - }, - name: options.name || `HTTPS (passthrough): ${Array.isArray(domains) ? domains.join(', ') : domains}` - }; - - return mergeRouteConfigs(route, options); -} - -/** - * Create an HTTP to HTTPS redirect route - */ -export function createHttpToHttpsRedirect( - domains: string | string[], - options: Partial & { - redirectCode?: 301 | 302 | 307 | 308; - preservePath?: boolean; - } = {} -): IRouteConfig { - const route: IRouteConfig = { - match: { - domains, - ports: 80 - }, - action: { - type: 'socket-handler', - socketHandler: SocketHandlers.httpRedirect( - options.preservePath ? 'https://{domain}{path}' : 'https://{domain}', - options.redirectCode || 301 - ) - }, - name: options.name || `HTTP to HTTPS redirect: ${Array.isArray(domains) ? domains.join(', ') : domains}` - }; - - return mergeRouteConfigs(route, options); -} - -/** - * Create a complete HTTPS server with redirect from HTTP - */ -export function createCompleteHttpsServer( - domains: string | string[], - target: { host: string | string[]; port: number | 'preserve' | ((ctx: any) => number) }, - options: Partial & { - certificate?: 'auto' | { key: string; cert: string }; - tlsMode?: 'terminate' | 'passthrough' | 'terminate-and-reencrypt'; - redirectCode?: 301 | 302 | 307 | 308; - } = {} -): IRouteConfig[] { - // Create the TLS route based on the selected mode - const tlsRoute = options.tlsMode === 'passthrough' - ? createHttpsPassthroughRoute(domains, target, options) - : createHttpsTerminateRoute(domains, target, { - ...options, - reencrypt: options.tlsMode === 'terminate-and-reencrypt' - }); - - // Create the HTTP to HTTPS redirect route - const redirectRoute = createHttpToHttpsRedirect(domains, { - redirectCode: options.redirectCode, - preservePath: true - }); - - return [tlsRoute, redirectRoute]; -} - -/** - * Create an API Gateway route pattern - * @param domains Domain(s) to match - * @param apiBasePath Base path for API endpoints (e.g., '/api') - * @param target Target host and port - * @param options Additional route options - * @returns API route configuration - */ -export function createApiGatewayRoute( - domains: string | string[], - apiBasePath: string, - target: { host: string | string[]; port: number }, - options: { - useTls?: boolean; - certificate?: 'auto' | { key: string; cert: string }; - addCorsHeaders?: boolean; - [key: string]: any; - } = {} -): IRouteConfig { - // Normalize apiBasePath to ensure it starts with / and doesn't end with / - const normalizedPath = apiBasePath.startsWith('/') - ? apiBasePath - : `/${apiBasePath}`; - - // Add wildcard to path to match all API endpoints - const apiPath = normalizedPath.endsWith('/') - ? `${normalizedPath}*` - : `${normalizedPath}/*`; - - // Create base route - const baseRoute = options.useTls - ? createHttpsTerminateRoute(domains, target, { - certificate: options.certificate || 'auto' - }) - : createHttpRoute(domains, target); - - // Add API-specific configurations - const apiRoute: Partial = { - match: { - ...baseRoute.match, - path: apiPath - }, - name: options.name || `API Gateway: ${apiPath} -> ${Array.isArray(target.host) ? target.host.join(', ') : target.host}:${target.port}`, - priority: options.priority || 100 // Higher priority for specific path matching - }; - - // Add CORS headers if requested - if (options.addCorsHeaders) { - apiRoute.headers = { - response: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'Access-Control-Allow-Headers': 'Content-Type, Authorization', - 'Access-Control-Max-Age': '86400' - } - }; - } - - return mergeRouteConfigs(baseRoute, apiRoute); -} - -/** - * Create a WebSocket route pattern - * @param domains Domain(s) to match - * @param target WebSocket server host and port - * @param options Additional route options - * @returns WebSocket route configuration - */ -export function createWebSocketRoute( - domains: string | string[], - target: { host: string | string[]; port: number }, - options: { - useTls?: boolean; - certificate?: 'auto' | { key: string; cert: string }; - path?: string; - [key: string]: any; - } = {} -): IRouteConfig { - // Create base route - const baseRoute = options.useTls - ? createHttpsTerminateRoute(domains, target, { - certificate: options.certificate || 'auto' - }) - : createHttpRoute(domains, target); - - // Add WebSocket-specific configurations - const wsRoute: Partial = { - match: { - ...baseRoute.match, - path: options.path || '/ws', - headers: { - 'Upgrade': 'websocket' - } - }, - action: { - ...baseRoute.action, - websocket: { - enabled: true, - pingInterval: options.pingInterval || 30000, // 30 seconds - pingTimeout: options.pingTimeout || 5000 // 5 seconds - } - }, - name: options.name || `WebSocket: ${Array.isArray(domains) ? domains.join(', ') : domains} -> ${Array.isArray(target.host) ? target.host.join(', ') : target.host}:${target.port}`, - priority: options.priority || 100 // Higher priority for WebSocket routes - }; - - return mergeRouteConfigs(baseRoute, wsRoute); -} - -/** - * Create a load balancer route pattern - * @param domains Domain(s) to match - * @param backends Array of backend servers - * @param options Additional route options - * @returns Load balancer route configuration - */ -export function createLoadBalancerRoute( - domains: string | string[], - backends: Array<{ host: string; port: number }>, - options: { - useTls?: boolean; - certificate?: 'auto' | { key: string; cert: string }; - algorithm?: 'round-robin' | 'least-connections' | 'ip-hash'; - healthCheck?: { - path: string; - interval: number; - timeout: number; - unhealthyThreshold: number; - healthyThreshold: number; - }; - [key: string]: any; - } = {} -): IRouteConfig { - // Extract hosts and ensure all backends use the same port - const port = backends[0].port; - const hosts = backends.map(backend => backend.host); - - // Create route with multiple hosts for load balancing - const baseRoute = options.useTls - ? createHttpsTerminateRoute(domains, { host: hosts, port }, { - certificate: options.certificate || 'auto' - }) - : createHttpRoute(domains, { host: hosts, port }); - - // Add load balancing specific configurations - const lbRoute: Partial = { - action: { - ...baseRoute.action, - loadBalancing: { - algorithm: options.algorithm || 'round-robin', - healthCheck: options.healthCheck - } - }, - name: options.name || `Load Balancer: ${Array.isArray(domains) ? domains.join(', ') : domains}`, - priority: options.priority || 50 - }; - - return mergeRouteConfigs(baseRoute, lbRoute); -} - -/** - * Create a rate limiting route pattern - * @param baseRoute Base route to add rate limiting to - * @param rateLimit Rate limiting configuration - * @returns Route with rate limiting - */ -export function addRateLimiting( - baseRoute: IRouteConfig, - rateLimit: { - maxRequests: number; - window: number; // Time window in seconds - keyBy?: 'ip' | 'path' | 'header'; - headerName?: string; // Required if keyBy is 'header' - errorMessage?: string; - } -): IRouteConfig { - return mergeRouteConfigs(baseRoute, { - security: { - rateLimit: { - enabled: true, - maxRequests: rateLimit.maxRequests, - window: rateLimit.window, - keyBy: rateLimit.keyBy || 'ip', - headerName: rateLimit.headerName, - errorMessage: rateLimit.errorMessage || 'Rate limit exceeded. Please try again later.' - } - } - }); -} - -/** - * Create a basic authentication route pattern - * @param baseRoute Base route to add authentication to - * @param auth Authentication configuration - * @returns Route with basic authentication - */ -export function addBasicAuth( - baseRoute: IRouteConfig, - auth: { - users: Array<{ username: string; password: string }>; - realm?: string; - excludePaths?: string[]; - } -): IRouteConfig { - return mergeRouteConfigs(baseRoute, { - security: { - basicAuth: { - enabled: true, - users: auth.users, - realm: auth.realm || 'Restricted Area', - excludePaths: auth.excludePaths || [] - } - } - }); -} - -/** - * Create a JWT authentication route pattern - * @param baseRoute Base route to add JWT authentication to - * @param jwt JWT authentication configuration - * @returns Route with JWT authentication - */ -export function addJwtAuth( - baseRoute: IRouteConfig, - jwt: { - secret: string; - algorithm?: string; - issuer?: string; - audience?: string; - expiresIn?: number; // Time in seconds - excludePaths?: string[]; - } -): IRouteConfig { - return mergeRouteConfigs(baseRoute, { - security: { - jwtAuth: { - enabled: true, - secret: jwt.secret, - algorithm: jwt.algorithm || 'HS256', - issuer: jwt.issuer, - audience: jwt.audience, - expiresIn: jwt.expiresIn, - excludePaths: jwt.excludePaths || [] - } - } - }); -} \ No newline at end of file