@push.rocks/smartproxy 19.5.18 → 19.5.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist_ts/00_commitinfo_data.js +2 -2
  2. package/dist_ts/core/models/index.d.ts +2 -0
  3. package/dist_ts/core/models/index.js +3 -1
  4. package/dist_ts/core/models/socket-types.d.ts +14 -0
  5. package/dist_ts/core/models/socket-types.js +15 -0
  6. package/dist_ts/core/models/wrapped-socket.d.ts +34 -0
  7. package/dist_ts/core/models/wrapped-socket.js +82 -0
  8. package/dist_ts/core/routing/index.d.ts +11 -0
  9. package/dist_ts/core/routing/index.js +17 -0
  10. package/dist_ts/core/routing/matchers/domain.d.ts +34 -0
  11. package/dist_ts/core/routing/matchers/domain.js +91 -0
  12. package/dist_ts/core/routing/matchers/header.d.ts +32 -0
  13. package/dist_ts/core/routing/matchers/header.js +94 -0
  14. package/dist_ts/core/routing/matchers/index.d.ts +18 -0
  15. package/dist_ts/core/routing/matchers/index.js +20 -0
  16. package/dist_ts/core/routing/matchers/ip.d.ts +53 -0
  17. package/dist_ts/core/routing/matchers/ip.js +169 -0
  18. package/dist_ts/core/routing/matchers/path.d.ts +44 -0
  19. package/dist_ts/core/routing/matchers/path.js +148 -0
  20. package/dist_ts/core/routing/route-manager.d.ts +88 -0
  21. package/dist_ts/core/routing/route-manager.js +342 -0
  22. package/dist_ts/core/routing/route-utils.d.ts +28 -0
  23. package/dist_ts/core/routing/route-utils.js +67 -0
  24. package/dist_ts/core/routing/specificity.d.ts +30 -0
  25. package/dist_ts/core/routing/specificity.js +115 -0
  26. package/dist_ts/core/routing/types.d.ts +41 -0
  27. package/dist_ts/core/routing/types.js +5 -0
  28. package/dist_ts/core/utils/index.d.ts +0 -2
  29. package/dist_ts/core/utils/index.js +1 -3
  30. package/dist_ts/core/utils/route-manager.d.ts +0 -30
  31. package/dist_ts/core/utils/route-manager.js +6 -47
  32. package/dist_ts/core/utils/route-utils.d.ts +2 -68
  33. package/dist_ts/core/utils/route-utils.js +21 -218
  34. package/dist_ts/core/utils/security-utils.js +4 -4
  35. package/dist_ts/core/utils/socket-utils.d.ts +0 -15
  36. package/dist_ts/core/utils/socket-utils.js +1 -35
  37. package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +47 -32
  38. package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +51 -32
  39. package/dist_ts/index.d.ts +2 -5
  40. package/dist_ts/index.js +5 -11
  41. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -1
  42. package/dist_ts/proxies/http-proxy/http-proxy.js +15 -60
  43. package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -90
  44. package/dist_ts/proxies/http-proxy/models/types.js +1 -242
  45. package/dist_ts/proxies/http-proxy/request-handler.d.ts +3 -5
  46. package/dist_ts/proxies/http-proxy/request-handler.js +20 -171
  47. package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +2 -5
  48. package/dist_ts/proxies/http-proxy/websocket-handler.js +15 -23
  49. package/dist_ts/proxies/index.d.ts +2 -2
  50. package/dist_ts/proxies/index.js +4 -3
  51. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +3 -1
  52. package/dist_ts/proxies/smart-proxy/connection-manager.js +15 -7
  53. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +2 -1
  54. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -2
  55. package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
  56. package/dist_ts/proxies/smart-proxy/index.js +2 -2
  57. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +6 -2
  58. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +1 -1
  59. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +48 -25
  60. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
  61. package/dist_ts/proxies/smart-proxy/smart-proxy.js +15 -4
  62. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +10 -43
  63. package/dist_ts/routing/router/http-router.d.ts +89 -0
  64. package/dist_ts/routing/router/http-router.js +205 -0
  65. package/dist_ts/routing/router/index.d.ts +2 -5
  66. package/dist_ts/routing/router/index.js +3 -4
  67. package/package.json +1 -1
  68. package/readme.delete.md +187 -0
  69. package/readme.hints.md +210 -1
  70. package/readme.plan.md +621 -0
  71. package/readme.routing.md +341 -0
  72. package/ts/00_commitinfo_data.ts +1 -1
  73. package/ts/core/models/index.ts +2 -0
  74. package/ts/core/models/socket-types.ts +21 -0
  75. package/ts/core/models/wrapped-socket.ts +99 -0
  76. package/ts/core/routing/index.ts +21 -0
  77. package/ts/core/routing/matchers/domain.ts +119 -0
  78. package/ts/core/routing/matchers/header.ts +120 -0
  79. package/ts/core/routing/matchers/index.ts +22 -0
  80. package/ts/core/routing/matchers/ip.ts +207 -0
  81. package/ts/core/routing/matchers/path.ts +184 -0
  82. package/ts/core/{utils → routing}/route-manager.ts +7 -57
  83. package/ts/core/routing/route-utils.ts +88 -0
  84. package/ts/core/routing/specificity.ts +141 -0
  85. package/ts/core/routing/types.ts +49 -0
  86. package/ts/core/utils/index.ts +0 -2
  87. package/ts/core/utils/security-utils.ts +3 -7
  88. package/ts/core/utils/socket-utils.ts +0 -44
  89. package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +47 -33
  90. package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +55 -35
  91. package/ts/index.ts +4 -14
  92. package/ts/proxies/http-proxy/http-proxy.ts +13 -68
  93. package/ts/proxies/http-proxy/models/types.ts +0 -324
  94. package/ts/proxies/http-proxy/request-handler.ts +15 -186
  95. package/ts/proxies/http-proxy/websocket-handler.ts +15 -26
  96. package/ts/proxies/index.ts +3 -2
  97. package/ts/proxies/smart-proxy/connection-manager.ts +15 -7
  98. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +6 -2
  99. package/ts/proxies/smart-proxy/index.ts +1 -1
  100. package/ts/proxies/smart-proxy/models/interfaces.ts +8 -2
  101. package/ts/proxies/smart-proxy/route-connection-handler.ts +58 -30
  102. package/ts/proxies/smart-proxy/smart-proxy.ts +15 -3
  103. package/ts/proxies/smart-proxy/utils/route-utils.ts +11 -49
  104. package/ts/routing/router/http-router.ts +266 -0
  105. package/ts/routing/router/index.ts +3 -8
  106. package/readme.problems.md +0 -170
  107. package/ts/core/utils/route-utils.ts +0 -312
  108. package/ts/proxies/smart-proxy/route-manager.ts +0 -554
  109. package/ts/routing/router/proxy-router.ts +0 -437
  110. package/ts/routing/router/route-router.ts +0 -482
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Route matching utilities for SmartProxy components
3
+ *
4
+ * This file provides utility functions that use the unified matchers
5
+ * and additional route-specific utilities.
6
+ */
7
+
8
+ import { DomainMatcher, PathMatcher, IpMatcher, HeaderMatcher } from './matchers/index.js';
9
+ import { RouteSpecificity } from './specificity.js';
10
+ import type { IRouteSpecificity } from './types.js';
11
+ import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
12
+
13
+
14
+ /**
15
+ * Match domains from a route against a given domain
16
+ *
17
+ * @param domains Array or single domain pattern to match against
18
+ * @param domain Domain to match
19
+ * @returns Whether the domain matches any of the patterns
20
+ */
21
+ export function matchRouteDomain(domains: string | string[] | undefined, domain: string | undefined): boolean {
22
+ // If no domains specified in the route, match all domains
23
+ if (!domains) {
24
+ return true;
25
+ }
26
+
27
+ // If no domain in the request, can't match domain-specific routes
28
+ if (!domain) {
29
+ return false;
30
+ }
31
+
32
+ const patterns = Array.isArray(domains) ? domains : [domains];
33
+ return patterns.some(pattern => DomainMatcher.match(pattern, domain));
34
+ }
35
+
36
+
37
+
38
+ /**
39
+ * Calculate route specificity score
40
+ * Higher score means more specific matching criteria
41
+ *
42
+ * @param match Match criteria to evaluate
43
+ * @returns Numeric specificity score
44
+ */
45
+ export function calculateRouteSpecificity(match: {
46
+ domains?: string | string[];
47
+ path?: string;
48
+ clientIp?: string[];
49
+ tlsVersion?: string[];
50
+ headers?: Record<string, string | RegExp>;
51
+ }): number {
52
+ let score = 0;
53
+
54
+ // Path specificity using PathMatcher
55
+ if (match.path) {
56
+ score += PathMatcher.calculateSpecificity(match.path);
57
+ }
58
+
59
+ // Domain specificity using DomainMatcher
60
+ if (match.domains) {
61
+ const domains = Array.isArray(match.domains) ? match.domains : [match.domains];
62
+ // Use the highest specificity among all domains
63
+ const domainScore = Math.max(...domains.map(d => DomainMatcher.calculateSpecificity(d)));
64
+ score += domainScore;
65
+ }
66
+
67
+ // Headers specificity using HeaderMatcher
68
+ if (match.headers) {
69
+ const stringHeaders: Record<string, string> = {};
70
+ for (const [key, value] of Object.entries(match.headers)) {
71
+ stringHeaders[key] = value instanceof RegExp ? value.source : value;
72
+ }
73
+ score += HeaderMatcher.calculateSpecificity(stringHeaders);
74
+ }
75
+
76
+ // Client IP adds some specificity
77
+ if (match.clientIp && match.clientIp.length > 0) {
78
+ // Use the first IP pattern for specificity
79
+ score += IpMatcher.calculateSpecificity(match.clientIp[0]);
80
+ }
81
+
82
+ // TLS version adds minimal specificity
83
+ if (match.tlsVersion && match.tlsVersion.length > 0) {
84
+ score += match.tlsVersion.length * 10;
85
+ }
86
+
87
+ return score;
88
+ }
@@ -0,0 +1,141 @@
1
+ import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
2
+ import type { IRouteSpecificity } from './types.js';
3
+ import { DomainMatcher, PathMatcher, IpMatcher, HeaderMatcher } from './matchers/index.js';
4
+
5
+ /**
6
+ * Unified route specificity calculator
7
+ * Provides consistent specificity scoring across all routing components
8
+ */
9
+ export class RouteSpecificity {
10
+ /**
11
+ * Calculate the total specificity score for a route
12
+ * Higher scores indicate more specific routes that should match first
13
+ */
14
+ static calculate(route: IRouteConfig): IRouteSpecificity {
15
+ const specificity: IRouteSpecificity = {
16
+ pathSpecificity: 0,
17
+ domainSpecificity: 0,
18
+ ipSpecificity: 0,
19
+ headerSpecificity: 0,
20
+ tlsSpecificity: 0,
21
+ totalScore: 0
22
+ };
23
+
24
+ // Path specificity
25
+ if (route.match.path) {
26
+ specificity.pathSpecificity = PathMatcher.calculateSpecificity(route.match.path);
27
+ }
28
+
29
+ // Domain specificity
30
+ if (route.match.domains) {
31
+ const domains = Array.isArray(route.match.domains)
32
+ ? route.match.domains
33
+ : [route.match.domains];
34
+
35
+ // Use the highest specificity among all domains
36
+ specificity.domainSpecificity = Math.max(
37
+ ...domains.map(d => DomainMatcher.calculateSpecificity(d))
38
+ );
39
+ }
40
+
41
+ // IP specificity (clientIp is an array of IPs)
42
+ if (route.match.clientIp && route.match.clientIp.length > 0) {
43
+ // Use the first IP pattern for specificity calculation
44
+ specificity.ipSpecificity = IpMatcher.calculateSpecificity(route.match.clientIp[0]);
45
+ }
46
+
47
+ // Header specificity (convert RegExp values to strings)
48
+ if (route.match.headers) {
49
+ const stringHeaders: Record<string, string> = {};
50
+ for (const [key, value] of Object.entries(route.match.headers)) {
51
+ stringHeaders[key] = value instanceof RegExp ? value.source : value;
52
+ }
53
+ specificity.headerSpecificity = HeaderMatcher.calculateSpecificity(stringHeaders);
54
+ }
55
+
56
+ // TLS version specificity
57
+ if (route.match.tlsVersion && route.match.tlsVersion.length > 0) {
58
+ specificity.tlsSpecificity = route.match.tlsVersion.length * 10;
59
+ }
60
+
61
+ // Calculate total score with weights
62
+ specificity.totalScore =
63
+ specificity.pathSpecificity * 3 + // Path is most important
64
+ specificity.domainSpecificity * 2 + // Domain is second
65
+ specificity.ipSpecificity * 1.5 + // IP is moderately important
66
+ specificity.headerSpecificity * 1 + // Headers are less important
67
+ specificity.tlsSpecificity * 0.5; // TLS is least important
68
+
69
+ return specificity;
70
+ }
71
+
72
+ /**
73
+ * Compare two routes and determine which is more specific
74
+ * @returns positive if route1 is more specific, negative if route2 is more specific, 0 if equal
75
+ */
76
+ static compare(route1: IRouteConfig, route2: IRouteConfig): number {
77
+ const spec1 = this.calculate(route1);
78
+ const spec2 = this.calculate(route2);
79
+
80
+ // First compare by total score
81
+ if (spec1.totalScore !== spec2.totalScore) {
82
+ return spec1.totalScore - spec2.totalScore;
83
+ }
84
+
85
+ // If total scores are equal, compare by individual components
86
+ // Path is most important tiebreaker
87
+ if (spec1.pathSpecificity !== spec2.pathSpecificity) {
88
+ return spec1.pathSpecificity - spec2.pathSpecificity;
89
+ }
90
+
91
+ // Then domain
92
+ if (spec1.domainSpecificity !== spec2.domainSpecificity) {
93
+ return spec1.domainSpecificity - spec2.domainSpecificity;
94
+ }
95
+
96
+ // Then IP
97
+ if (spec1.ipSpecificity !== spec2.ipSpecificity) {
98
+ return spec1.ipSpecificity - spec2.ipSpecificity;
99
+ }
100
+
101
+ // Then headers
102
+ if (spec1.headerSpecificity !== spec2.headerSpecificity) {
103
+ return spec1.headerSpecificity - spec2.headerSpecificity;
104
+ }
105
+
106
+ // Finally TLS
107
+ return spec1.tlsSpecificity - spec2.tlsSpecificity;
108
+ }
109
+
110
+ /**
111
+ * Sort routes by specificity (most specific first)
112
+ */
113
+ static sort(routes: IRouteConfig[]): IRouteConfig[] {
114
+ return [...routes].sort((a, b) => this.compare(b, a));
115
+ }
116
+
117
+ /**
118
+ * Find the most specific route from a list
119
+ */
120
+ static findMostSpecific(routes: IRouteConfig[]): IRouteConfig | null {
121
+ if (routes.length === 0) return null;
122
+
123
+ return routes.reduce((most, current) =>
124
+ this.compare(current, most) > 0 ? current : most
125
+ );
126
+ }
127
+
128
+ /**
129
+ * Check if a route has any matching criteria
130
+ */
131
+ static hasMatchCriteria(route: IRouteConfig): boolean {
132
+ const match = route.match;
133
+ return !!(
134
+ match.domains ||
135
+ match.path ||
136
+ match.clientIp?.length ||
137
+ match.headers ||
138
+ match.tlsVersion?.length
139
+ );
140
+ }
141
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Core routing types used throughout the routing system
3
+ */
4
+
5
+ export interface IPathMatchResult {
6
+ matches: boolean;
7
+ params?: Record<string, string>;
8
+ pathMatch?: string;
9
+ pathRemainder?: string;
10
+ }
11
+
12
+ export interface IRouteMatchResult {
13
+ matches: boolean;
14
+ score: number;
15
+ specificity: number;
16
+ matchedCriteria: string[];
17
+ }
18
+
19
+ export interface IDomainMatchOptions {
20
+ allowWildcards?: boolean;
21
+ caseInsensitive?: boolean;
22
+ }
23
+
24
+ export interface IIpMatchOptions {
25
+ allowCidr?: boolean;
26
+ allowRanges?: boolean;
27
+ }
28
+
29
+ export interface IHeaderMatchOptions {
30
+ caseInsensitive?: boolean;
31
+ exactMatch?: boolean;
32
+ }
33
+
34
+ export interface IRouteSpecificity {
35
+ pathSpecificity: number;
36
+ domainSpecificity: number;
37
+ ipSpecificity: number;
38
+ headerSpecificity: number;
39
+ tlsSpecificity: number;
40
+ totalScore: number;
41
+ }
42
+
43
+ export interface IMatcher<T = any, O = any> {
44
+ match(pattern: string, value: string, options?: O): T | boolean;
45
+ }
46
+
47
+ export interface IAsyncMatcher<T = any, O = any> {
48
+ match(pattern: string, value: string, options?: O): Promise<T | boolean>;
49
+ }
@@ -5,8 +5,6 @@
5
5
  export * from './validation-utils.js';
6
6
  export * from './ip-utils.js';
7
7
  export * from './template-utils.js';
8
- export * from './route-manager.js';
9
- export * from './route-utils.js';
10
8
  export * from './security-utils.js';
11
9
  export * from './shared-security-manager.js';
12
10
  export * from './websocket-utils.js';
@@ -1,9 +1,5 @@
1
1
  import * as plugins from '../../plugins.js';
2
- import {
3
- matchIpPattern,
4
- ipToNumber,
5
- matchIpCidr
6
- } from './route-utils.js';
2
+ import { IpMatcher } from '../routing/matchers/ip.js';
7
3
 
8
4
  /**
9
5
  * Security utilities for IP validation, rate limiting,
@@ -90,7 +86,7 @@ export function isIPAuthorized(
90
86
  // First check if IP is blocked - blocked IPs take precedence
91
87
  if (blockedIPs.length > 0) {
92
88
  for (const pattern of blockedIPs) {
93
- if (matchIpPattern(pattern, ip)) {
89
+ if (IpMatcher.match(pattern, ip)) {
94
90
  return false;
95
91
  }
96
92
  }
@@ -104,7 +100,7 @@ export function isIPAuthorized(
104
100
  // Then check if IP is allowed in the explicit allow list
105
101
  if (allowedIPs.length > 0) {
106
102
  for (const pattern of allowedIPs) {
107
- if (matchIpPattern(pattern, ip)) {
103
+ if (IpMatcher.match(pattern, ip)) {
108
104
  return true;
109
105
  }
110
106
  }
@@ -67,37 +67,6 @@ export function cleanupSocket(
67
67
  });
68
68
  }
69
69
 
70
- /**
71
- * Create a cleanup handler for paired sockets (client and server)
72
- * @param clientSocket The client socket
73
- * @param serverSocket The server socket (optional)
74
- * @param onCleanup Optional callback when cleanup is done
75
- * @returns A cleanup function that can be called multiple times safely
76
- * @deprecated Use createIndependentSocketHandlers for better half-open support
77
- */
78
- export function createSocketCleanupHandler(
79
- clientSocket: plugins.net.Socket | plugins.tls.TLSSocket,
80
- serverSocket?: plugins.net.Socket | plugins.tls.TLSSocket | null,
81
- onCleanup?: (reason: string) => void
82
- ): (reason: string) => void {
83
- let cleanedUp = false;
84
-
85
- return (reason: string) => {
86
- if (cleanedUp) return;
87
- cleanedUp = true;
88
-
89
- // Cleanup both sockets (old behavior - too aggressive)
90
- cleanupSocket(clientSocket, 'client', { immediate: true });
91
- if (serverSocket) {
92
- cleanupSocket(serverSocket, 'server', { immediate: true });
93
- }
94
-
95
- // Call cleanup callback if provided
96
- if (onCleanup) {
97
- onCleanup(reason);
98
- }
99
- };
100
- }
101
70
 
102
71
  /**
103
72
  * Create independent cleanup handlers for paired sockets that support half-open connections
@@ -278,19 +247,6 @@ export function setupBidirectionalForwarding(
278
247
  return { cleanupClient, cleanupServer };
279
248
  }
280
249
 
281
- /**
282
- * Pipe two sockets together with proper cleanup on either end
283
- * @param socket1 First socket
284
- * @param socket2 Second socket
285
- */
286
- export function pipeSockets(
287
- socket1: plugins.net.Socket | plugins.tls.TLSSocket,
288
- socket2: plugins.net.Socket | plugins.tls.TLSSocket
289
- ): void {
290
- socket1.pipe(socket2);
291
- socket2.pipe(socket1);
292
- }
293
-
294
250
  /**
295
251
  * Create a socket with immediate error handling to prevent crashes
296
252
  * @param options Socket creation options
@@ -2,7 +2,7 @@ import * as plugins from '../../plugins.js';
2
2
  import { ForwardingHandler } from './base-handler.js';
3
3
  import type { IForwardConfig } from '../config/forwarding-types.js';
4
4
  import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
5
- import { createSocketCleanupHandler, setupSocketHandlers, createSocketWithErrorHandler } from '../../core/utils/socket-utils.js';
5
+ import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
6
6
 
7
7
  /**
8
8
  * Handler for HTTPS termination with HTTP backend
@@ -100,19 +100,30 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
100
100
  let backendSocket: plugins.net.Socket | null = null;
101
101
  let dataBuffer = Buffer.alloc(0);
102
102
  let connectionEstablished = false;
103
+ let forwardingSetup = false;
103
104
 
104
- // Create cleanup handler for all sockets
105
- const handleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
106
- this.emit(ForwardingHandlerEvents.DISCONNECTED, {
107
- remoteAddress,
108
- reason
109
- });
110
- dataBuffer = Buffer.alloc(0);
111
- connectionEstablished = false;
112
- });
105
+ // Set up initial error handling for TLS socket
106
+ const tlsCleanupHandler = (reason: string) => {
107
+ if (!forwardingSetup) {
108
+ // If forwarding not set up yet, emit disconnected and cleanup
109
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
110
+ remoteAddress,
111
+ reason
112
+ });
113
+ dataBuffer = Buffer.alloc(0);
114
+ connectionEstablished = false;
115
+
116
+ if (!tlsSocket.destroyed) {
117
+ tlsSocket.destroy();
118
+ }
119
+ if (backendSocket && !backendSocket.destroyed) {
120
+ backendSocket.destroy();
121
+ }
122
+ }
123
+ // If forwarding is setup, setupBidirectionalForwarding will handle cleanup
124
+ };
113
125
 
114
- // Set up error handling with our cleanup utility
115
- setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls');
126
+ setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
116
127
 
117
128
  // Set timeout
118
129
  const timeout = this.getTimeout();
@@ -123,7 +134,7 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
123
134
  remoteAddress,
124
135
  error: 'TLS connection timeout'
125
136
  });
126
- handleClose('timeout');
137
+ tlsCleanupHandler('timeout');
127
138
  });
128
139
 
129
140
  // Handle TLS data
@@ -172,30 +183,33 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
172
183
  dataBuffer = Buffer.alloc(0);
173
184
  }
174
185
 
175
- // Set up bidirectional data flow
176
- tlsSocket.pipe(backendSocket!);
177
- backendSocket!.pipe(tlsSocket);
186
+ // Now set up bidirectional forwarding with proper cleanup
187
+ forwardingSetup = true;
188
+ setupBidirectionalForwarding(tlsSocket, backendSocket!, {
189
+ onCleanup: (reason) => {
190
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
191
+ remoteAddress,
192
+ reason
193
+ });
194
+ dataBuffer = Buffer.alloc(0);
195
+ connectionEstablished = false;
196
+ forwardingSetup = false;
197
+ },
198
+ enableHalfOpen: false // Close both when one closes
199
+ });
178
200
  }
179
201
  });
180
202
 
181
- // Update the cleanup handler with the backend socket
182
- const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
183
- this.emit(ForwardingHandlerEvents.DISCONNECTED, {
184
- remoteAddress,
185
- reason
186
- });
187
- dataBuffer = Buffer.alloc(0);
188
- connectionEstablished = false;
189
- });
190
-
191
- // Set up handlers for backend socket
192
- setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
193
-
203
+ // Additional error logging for backend socket
194
204
  backendSocket.on('error', (error) => {
195
- this.emit(ForwardingHandlerEvents.ERROR, {
196
- remoteAddress,
197
- error: `Target connection error: ${error.message}`
198
- });
205
+ if (!connectionEstablished) {
206
+ // Connection failed during setup
207
+ this.emit(ForwardingHandlerEvents.ERROR, {
208
+ remoteAddress,
209
+ error: `Target connection error: ${error.message}`
210
+ });
211
+ }
212
+ // If connected, setupBidirectionalForwarding handles cleanup
199
213
  });
200
214
  }
201
215
  });
@@ -2,7 +2,7 @@ import * as plugins from '../../plugins.js';
2
2
  import { ForwardingHandler } from './base-handler.js';
3
3
  import type { IForwardConfig } from '../config/forwarding-types.js';
4
4
  import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
5
- import { createSocketCleanupHandler, setupSocketHandlers, createSocketWithErrorHandler } from '../../core/utils/socket-utils.js';
5
+ import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
6
6
 
7
7
  /**
8
8
  * Handler for HTTPS termination with HTTPS backend
@@ -96,17 +96,26 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
96
96
 
97
97
  // Variable to track backend socket
98
98
  let backendSocket: plugins.tls.TLSSocket | null = null;
99
+ let isConnectedToBackend = false;
99
100
 
100
- // Create cleanup handler for both sockets
101
- const handleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
102
- this.emit(ForwardingHandlerEvents.DISCONNECTED, {
103
- remoteAddress,
104
- reason
105
- });
106
- });
101
+ // Set up initial error handling for TLS socket
102
+ const tlsCleanupHandler = (reason: string) => {
103
+ if (!isConnectedToBackend) {
104
+ // If backend not connected yet, just emit disconnected event
105
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
106
+ remoteAddress,
107
+ reason
108
+ });
109
+
110
+ // Cleanup TLS socket if needed
111
+ if (!tlsSocket.destroyed) {
112
+ tlsSocket.destroy();
113
+ }
114
+ }
115
+ // If connected to backend, setupBidirectionalForwarding will handle cleanup
116
+ };
107
117
 
108
- // Set up error handling with our cleanup utility
109
- setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls');
118
+ setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
110
119
 
111
120
  // Set timeout
112
121
  const timeout = this.getTimeout();
@@ -117,7 +126,7 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
117
126
  remoteAddress,
118
127
  error: 'TLS connection timeout'
119
128
  });
120
- handleClose('timeout');
129
+ tlsCleanupHandler('timeout');
121
130
  });
122
131
 
123
132
  // Get the target from configuration
@@ -131,44 +140,55 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
131
140
  // In a real implementation, we would configure TLS options
132
141
  rejectUnauthorized: false // For testing only, never use in production
133
142
  }, () => {
143
+ isConnectedToBackend = true;
144
+
134
145
  this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
135
146
  direction: 'outbound',
136
147
  target: `${target.host}:${target.port}`,
137
148
  tls: true
138
149
  });
139
150
 
140
- // Set up bidirectional data flow
141
- tlsSocket.pipe(backendSocket!);
142
- backendSocket!.pipe(tlsSocket);
143
- });
144
-
145
- // Update the cleanup handler with the backend socket
146
- const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
147
- this.emit(ForwardingHandlerEvents.DISCONNECTED, {
148
- remoteAddress,
149
- reason
151
+ // Set up bidirectional forwarding with proper cleanup
152
+ setupBidirectionalForwarding(tlsSocket, backendSocket!, {
153
+ onCleanup: (reason) => {
154
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
155
+ remoteAddress,
156
+ reason
157
+ });
158
+ },
159
+ enableHalfOpen: false // Close both when one closes
160
+ });
161
+
162
+ // Set timeout for backend socket
163
+ backendSocket!.setTimeout(timeout);
164
+
165
+ backendSocket!.on('timeout', () => {
166
+ this.emit(ForwardingHandlerEvents.ERROR, {
167
+ remoteAddress,
168
+ error: 'Backend connection timeout'
169
+ });
170
+ // Let setupBidirectionalForwarding handle the cleanup
150
171
  });
151
172
  });
152
173
 
153
- // Set up handlers for backend socket
154
- setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
155
-
174
+ // Handle backend connection errors
156
175
  backendSocket.on('error', (error) => {
157
176
  this.emit(ForwardingHandlerEvents.ERROR, {
158
177
  remoteAddress,
159
178
  error: `Backend connection error: ${error.message}`
160
179
  });
161
- });
162
-
163
- // Set timeout for backend socket
164
- backendSocket.setTimeout(timeout);
165
-
166
- backendSocket.on('timeout', () => {
167
- this.emit(ForwardingHandlerEvents.ERROR, {
168
- remoteAddress,
169
- error: 'Backend connection timeout'
170
- });
171
- newHandleClose('backend_timeout');
180
+
181
+ if (!isConnectedToBackend) {
182
+ // Connection failed, clean up TLS socket
183
+ if (!tlsSocket.destroyed) {
184
+ tlsSocket.destroy();
185
+ }
186
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
187
+ remoteAddress,
188
+ reason: `backend_connection_failed: ${error.message}`
189
+ });
190
+ }
191
+ // If connected, let setupBidirectionalForwarding handle cleanup
172
192
  });
173
193
  };
174
194
 
package/ts/index.ts CHANGED
@@ -2,28 +2,18 @@
2
2
  * SmartProxy main module exports
3
3
  */
4
4
 
5
- // Legacy exports (to maintain backward compatibility)
6
- // Migrated to the new proxies structure
5
+ // NFTables proxy exports
7
6
  export * from './proxies/nftables-proxy/index.js';
8
7
 
9
- // Export HttpProxy elements selectively to avoid RouteManager ambiguity
8
+ // Export HttpProxy elements
10
9
  export { HttpProxy, CertificateManager, ConnectionPool, RequestHandler, WebSocketHandler } from './proxies/http-proxy/index.js';
11
10
  export type { IMetricsTracker, MetricsTracker } from './proxies/http-proxy/index.js';
12
- // Export models except IAcmeOptions to avoid conflict
13
11
  export type { IHttpProxyOptions, ICertificateEntry, ILogger } from './proxies/http-proxy/models/types.js';
14
- export { RouteManager as HttpProxyRouteManager } from './proxies/http-proxy/models/types.js';
15
-
16
- // Backward compatibility exports (deprecated)
17
- export { HttpProxy as NetworkProxy } from './proxies/http-proxy/index.js';
18
- export type { IHttpProxyOptions as INetworkProxyOptions } from './proxies/http-proxy/models/types.js';
19
- export { HttpProxyBridge as NetworkProxyBridge } from './proxies/smart-proxy/index.js';
20
-
21
- // Certificate and Port80 modules have been removed - use SmartCertManager instead
22
- // Redirect module has been removed - use route-based redirects instead
12
+ export { SharedRouteManager as HttpProxyRouteManager } from './core/routing/route-manager.js';
23
13
 
24
14
  // Export SmartProxy elements selectively to avoid RouteManager ambiguity
25
15
  export { SmartProxy, ConnectionManager, SecurityManager, TimeoutManager, TlsManager, HttpProxyBridge, RouteConnectionHandler, SmartCertManager } from './proxies/smart-proxy/index.js';
26
- export { RouteManager } from './proxies/smart-proxy/route-manager.js';
16
+ export { SharedRouteManager as RouteManager } from './core/routing/route-manager.js';
27
17
  // Export smart-proxy models
28
18
  export type { ISmartProxyOptions, IConnectionRecord, IRouteConfig, IRouteMatch, IRouteAction, IRouteTls, IRouteContext } from './proxies/smart-proxy/models/index.js';
29
19
  export type { TSmartProxyCertProvisionObject } from './proxies/smart-proxy/models/interfaces.js';