@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.
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/core/models/index.d.ts +2 -0
- package/dist_ts/core/models/index.js +3 -1
- package/dist_ts/core/models/socket-types.d.ts +14 -0
- package/dist_ts/core/models/socket-types.js +15 -0
- package/dist_ts/core/models/wrapped-socket.d.ts +34 -0
- package/dist_ts/core/models/wrapped-socket.js +82 -0
- package/dist_ts/core/routing/index.d.ts +11 -0
- package/dist_ts/core/routing/index.js +17 -0
- package/dist_ts/core/routing/matchers/domain.d.ts +34 -0
- package/dist_ts/core/routing/matchers/domain.js +91 -0
- package/dist_ts/core/routing/matchers/header.d.ts +32 -0
- package/dist_ts/core/routing/matchers/header.js +94 -0
- package/dist_ts/core/routing/matchers/index.d.ts +18 -0
- package/dist_ts/core/routing/matchers/index.js +20 -0
- package/dist_ts/core/routing/matchers/ip.d.ts +53 -0
- package/dist_ts/core/routing/matchers/ip.js +169 -0
- package/dist_ts/core/routing/matchers/path.d.ts +44 -0
- package/dist_ts/core/routing/matchers/path.js +148 -0
- package/dist_ts/core/routing/route-manager.d.ts +88 -0
- package/dist_ts/core/routing/route-manager.js +342 -0
- package/dist_ts/core/routing/route-utils.d.ts +28 -0
- package/dist_ts/core/routing/route-utils.js +67 -0
- package/dist_ts/core/routing/specificity.d.ts +30 -0
- package/dist_ts/core/routing/specificity.js +115 -0
- package/dist_ts/core/routing/types.d.ts +41 -0
- package/dist_ts/core/routing/types.js +5 -0
- package/dist_ts/core/utils/index.d.ts +0 -2
- package/dist_ts/core/utils/index.js +1 -3
- package/dist_ts/core/utils/route-manager.d.ts +0 -30
- package/dist_ts/core/utils/route-manager.js +6 -47
- package/dist_ts/core/utils/route-utils.d.ts +2 -68
- package/dist_ts/core/utils/route-utils.js +21 -218
- package/dist_ts/core/utils/security-utils.js +4 -4
- package/dist_ts/core/utils/socket-utils.d.ts +0 -15
- package/dist_ts/core/utils/socket-utils.js +1 -35
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +47 -32
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +51 -32
- package/dist_ts/index.d.ts +2 -5
- package/dist_ts/index.js +5 -11
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -1
- package/dist_ts/proxies/http-proxy/http-proxy.js +15 -60
- package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -90
- package/dist_ts/proxies/http-proxy/models/types.js +1 -242
- package/dist_ts/proxies/http-proxy/request-handler.d.ts +3 -5
- package/dist_ts/proxies/http-proxy/request-handler.js +20 -171
- package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +2 -5
- package/dist_ts/proxies/http-proxy/websocket-handler.js +15 -23
- package/dist_ts/proxies/index.d.ts +2 -2
- package/dist_ts/proxies/index.js +4 -3
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +3 -1
- package/dist_ts/proxies/smart-proxy/connection-manager.js +15 -7
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +2 -1
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -2
- package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/index.js +2 -2
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +6 -2
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +48 -25
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +15 -4
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +10 -43
- package/dist_ts/routing/router/http-router.d.ts +89 -0
- package/dist_ts/routing/router/http-router.js +205 -0
- package/dist_ts/routing/router/index.d.ts +2 -5
- package/dist_ts/routing/router/index.js +3 -4
- package/package.json +1 -1
- package/readme.delete.md +187 -0
- package/readme.hints.md +210 -1
- package/readme.plan.md +621 -0
- package/readme.routing.md +341 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/models/index.ts +2 -0
- package/ts/core/models/socket-types.ts +21 -0
- package/ts/core/models/wrapped-socket.ts +99 -0
- package/ts/core/routing/index.ts +21 -0
- package/ts/core/routing/matchers/domain.ts +119 -0
- package/ts/core/routing/matchers/header.ts +120 -0
- package/ts/core/routing/matchers/index.ts +22 -0
- package/ts/core/routing/matchers/ip.ts +207 -0
- package/ts/core/routing/matchers/path.ts +184 -0
- package/ts/core/{utils → routing}/route-manager.ts +7 -57
- package/ts/core/routing/route-utils.ts +88 -0
- package/ts/core/routing/specificity.ts +141 -0
- package/ts/core/routing/types.ts +49 -0
- package/ts/core/utils/index.ts +0 -2
- package/ts/core/utils/security-utils.ts +3 -7
- package/ts/core/utils/socket-utils.ts +0 -44
- package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +47 -33
- package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +55 -35
- package/ts/index.ts +4 -14
- package/ts/proxies/http-proxy/http-proxy.ts +13 -68
- package/ts/proxies/http-proxy/models/types.ts +0 -324
- package/ts/proxies/http-proxy/request-handler.ts +15 -186
- package/ts/proxies/http-proxy/websocket-handler.ts +15 -26
- package/ts/proxies/index.ts +3 -2
- package/ts/proxies/smart-proxy/connection-manager.ts +15 -7
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +6 -2
- package/ts/proxies/smart-proxy/index.ts +1 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +8 -2
- package/ts/proxies/smart-proxy/route-connection-handler.ts +58 -30
- package/ts/proxies/smart-proxy/smart-proxy.ts +15 -3
- package/ts/proxies/smart-proxy/utils/route-utils.ts +11 -49
- package/ts/routing/router/http-router.ts +266 -0
- package/ts/routing/router/index.ts +3 -8
- package/readme.problems.md +0 -170
- package/ts/core/utils/route-utils.ts +0 -312
- package/ts/proxies/smart-proxy/route-manager.ts +0 -554
- package/ts/routing/router/proxy-router.ts +0 -437
- package/ts/routing/router/route-router.ts +0 -482
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# SmartProxy Routing Architecture Unification Plan
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This document analyzes the current state of routing in SmartProxy, identifies redundancies and inconsistencies, and proposes a unified architecture.
|
|
5
|
+
|
|
6
|
+
## Current State Analysis
|
|
7
|
+
|
|
8
|
+
### 1. Multiple Route Manager Implementations
|
|
9
|
+
|
|
10
|
+
#### 1.1 Core SharedRouteManager (`ts/core/utils/route-manager.ts`)
|
|
11
|
+
- **Purpose**: Designed as a shared component for SmartProxy and NetworkProxy
|
|
12
|
+
- **Features**:
|
|
13
|
+
- Port mapping and expansion (e.g., `[80, 443]` → individual routes)
|
|
14
|
+
- Comprehensive route matching (domain, path, IP, headers, TLS)
|
|
15
|
+
- Route validation and conflict detection
|
|
16
|
+
- Event emitter for route changes
|
|
17
|
+
- Detailed logging support
|
|
18
|
+
- **Status**: Well-designed but underutilized
|
|
19
|
+
|
|
20
|
+
#### 1.2 SmartProxy RouteManager (`ts/proxies/smart-proxy/route-manager.ts`)
|
|
21
|
+
- **Purpose**: SmartProxy-specific route management
|
|
22
|
+
- **Issues**:
|
|
23
|
+
- 95% duplicate code from SharedRouteManager
|
|
24
|
+
- Only difference is using `ISmartProxyOptions` instead of generic interface
|
|
25
|
+
- Contains deprecated security methods
|
|
26
|
+
- Unnecessary code duplication
|
|
27
|
+
- **Status**: Should be removed in favor of SharedRouteManager
|
|
28
|
+
|
|
29
|
+
#### 1.3 HttpProxy Route Management (`ts/proxies/http-proxy/`)
|
|
30
|
+
- **Purpose**: HTTP-specific routing
|
|
31
|
+
- **Implementation**: Minimal, inline route matching
|
|
32
|
+
- **Status**: Could benefit from SharedRouteManager
|
|
33
|
+
|
|
34
|
+
### 2. Multiple Router Implementations
|
|
35
|
+
|
|
36
|
+
#### 2.1 ProxyRouter (`ts/routing/router/proxy-router.ts`)
|
|
37
|
+
- **Purpose**: Legacy compatibility with `IReverseProxyConfig`
|
|
38
|
+
- **Features**: Domain-based routing with path patterns
|
|
39
|
+
- **Used by**: HttpProxy for backward compatibility
|
|
40
|
+
|
|
41
|
+
#### 2.2 RouteRouter (`ts/routing/router/route-router.ts`)
|
|
42
|
+
- **Purpose**: Modern routing with `IRouteConfig`
|
|
43
|
+
- **Features**: Nearly identical to ProxyRouter
|
|
44
|
+
- **Issues**: Code duplication with ProxyRouter
|
|
45
|
+
|
|
46
|
+
### 3. Scattered Route Utilities
|
|
47
|
+
|
|
48
|
+
#### 3.1 Core route-utils (`ts/core/utils/route-utils.ts`)
|
|
49
|
+
- **Purpose**: Shared matching functions
|
|
50
|
+
- **Features**: Domain, path, IP, CIDR matching
|
|
51
|
+
- **Status**: Well-implemented, should be the single source
|
|
52
|
+
|
|
53
|
+
#### 3.2 SmartProxy route-utils (`ts/proxies/smart-proxy/utils/route-utils.ts`)
|
|
54
|
+
- **Purpose**: Route configuration utilities
|
|
55
|
+
- **Features**: Different scope - config merging, not pattern matching
|
|
56
|
+
- **Status**: Keep separate as it serves different purpose
|
|
57
|
+
|
|
58
|
+
### 4. Other Route-Related Files
|
|
59
|
+
- `route-patterns.ts`: Constants for route patterns
|
|
60
|
+
- `route-validators.ts`: Route configuration validation
|
|
61
|
+
- `route-helpers.ts`: Additional utilities
|
|
62
|
+
- `route-connection-handler.ts`: Connection routing logic
|
|
63
|
+
|
|
64
|
+
## Problems Identified
|
|
65
|
+
|
|
66
|
+
### 1. Code Duplication
|
|
67
|
+
- **SharedRouteManager vs SmartProxy RouteManager**: ~1000 lines of duplicate code
|
|
68
|
+
- **ProxyRouter vs RouteRouter**: ~500 lines of duplicate code
|
|
69
|
+
- **Matching logic**: Implemented in 4+ different places
|
|
70
|
+
|
|
71
|
+
### 2. Inconsistent Implementations
|
|
72
|
+
```typescript
|
|
73
|
+
// Example: Domain matching appears in multiple places
|
|
74
|
+
// 1. In route-utils.ts
|
|
75
|
+
export function matchDomain(pattern: string, hostname: string): boolean
|
|
76
|
+
|
|
77
|
+
// 2. In SmartProxy RouteManager
|
|
78
|
+
private matchDomain(domain: string, hostname: string): boolean
|
|
79
|
+
|
|
80
|
+
// 3. In ProxyRouter
|
|
81
|
+
private matchesHostname(configName: string, hostname: string): boolean
|
|
82
|
+
|
|
83
|
+
// 4. In RouteRouter
|
|
84
|
+
private matchDomain(pattern: string, hostname: string): boolean
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. Unclear Separation of Concerns
|
|
88
|
+
- Route Managers handle both storage AND matching
|
|
89
|
+
- Routers also handle storage AND matching
|
|
90
|
+
- No clear boundaries between layers
|
|
91
|
+
|
|
92
|
+
### 4. Maintenance Burden
|
|
93
|
+
- Bug fixes need to be applied in multiple places
|
|
94
|
+
- New features must be implemented multiple times
|
|
95
|
+
- Testing effort multiplied
|
|
96
|
+
|
|
97
|
+
## Proposed Unified Architecture
|
|
98
|
+
|
|
99
|
+
### Layer 1: Core Routing Components
|
|
100
|
+
```
|
|
101
|
+
ts/core/routing/
|
|
102
|
+
├── types.ts # All route-related types
|
|
103
|
+
├── utils.ts # All matching logic (consolidated)
|
|
104
|
+
├── route-store.ts # Route storage and indexing
|
|
105
|
+
└── route-matcher.ts # Route matching engine
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Layer 2: Route Management
|
|
109
|
+
```
|
|
110
|
+
ts/core/routing/
|
|
111
|
+
└── route-manager.ts # Single RouteManager for all proxies
|
|
112
|
+
- Uses RouteStore for storage
|
|
113
|
+
- Uses RouteMatcher for matching
|
|
114
|
+
- Provides high-level API
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Layer 3: HTTP Routing
|
|
118
|
+
```
|
|
119
|
+
ts/routing/
|
|
120
|
+
└── http-router.ts # Single HTTP router implementation
|
|
121
|
+
- Uses RouteManager for route lookup
|
|
122
|
+
- Handles HTTP-specific concerns
|
|
123
|
+
- Legacy adapter built-in
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Layer 4: Proxy Integration
|
|
127
|
+
```
|
|
128
|
+
ts/proxies/
|
|
129
|
+
├── smart-proxy/
|
|
130
|
+
│ └── (uses core RouteManager directly)
|
|
131
|
+
├── http-proxy/
|
|
132
|
+
│ └── (uses core RouteManager + HttpRouter)
|
|
133
|
+
└── network-proxy/
|
|
134
|
+
└── (uses core RouteManager directly)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Implementation Plan
|
|
138
|
+
|
|
139
|
+
### Phase 1: Consolidate Matching Logic (Week 1)
|
|
140
|
+
1. **Audit all matching implementations**
|
|
141
|
+
- Document differences in behavior
|
|
142
|
+
- Identify the most comprehensive implementation
|
|
143
|
+
- Create test suite covering all edge cases
|
|
144
|
+
|
|
145
|
+
2. **Create unified matching module**
|
|
146
|
+
```typescript
|
|
147
|
+
// ts/core/routing/matchers.ts
|
|
148
|
+
export class DomainMatcher {
|
|
149
|
+
static match(pattern: string, hostname: string): boolean
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export class PathMatcher {
|
|
153
|
+
static match(pattern: string, path: string): MatchResult
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export class IpMatcher {
|
|
157
|
+
static match(pattern: string, ip: string): boolean
|
|
158
|
+
static matchCidr(cidr: string, ip: string): boolean
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
3. **Update all components to use unified matchers**
|
|
163
|
+
- Replace local implementations
|
|
164
|
+
- Ensure backward compatibility
|
|
165
|
+
- Run comprehensive tests
|
|
166
|
+
|
|
167
|
+
### Phase 2: Unify Route Managers (Week 2)
|
|
168
|
+
1. **Enhance SharedRouteManager**
|
|
169
|
+
- Add any missing features from SmartProxy RouteManager
|
|
170
|
+
- Make it truly generic (no proxy-specific dependencies)
|
|
171
|
+
- Add adapter pattern for different options types
|
|
172
|
+
|
|
173
|
+
2. **Migrate SmartProxy to use SharedRouteManager**
|
|
174
|
+
```typescript
|
|
175
|
+
// Before
|
|
176
|
+
this.routeManager = new RouteManager(this.settings);
|
|
177
|
+
|
|
178
|
+
// After
|
|
179
|
+
this.routeManager = new SharedRouteManager({
|
|
180
|
+
logger: this.settings.logger,
|
|
181
|
+
enableDetailedLogging: this.settings.enableDetailedLogging
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
3. **Remove duplicate RouteManager**
|
|
186
|
+
- Delete `ts/proxies/smart-proxy/route-manager.ts`
|
|
187
|
+
- Update all imports
|
|
188
|
+
- Verify all tests pass
|
|
189
|
+
|
|
190
|
+
### Phase 3: Consolidate Routers (Week 3)
|
|
191
|
+
1. **Create unified HttpRouter**
|
|
192
|
+
```typescript
|
|
193
|
+
export class HttpRouter {
|
|
194
|
+
constructor(private routeManager: SharedRouteManager) {}
|
|
195
|
+
|
|
196
|
+
// Modern interface
|
|
197
|
+
route(req: IncomingMessage): RouteResult
|
|
198
|
+
|
|
199
|
+
// Legacy adapter
|
|
200
|
+
routeLegacy(config: IReverseProxyConfig): RouteResult
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
2. **Migrate HttpProxy**
|
|
205
|
+
- Replace both ProxyRouter and RouteRouter
|
|
206
|
+
- Use single HttpRouter with appropriate adapter
|
|
207
|
+
- Maintain backward compatibility
|
|
208
|
+
|
|
209
|
+
3. **Clean up legacy code**
|
|
210
|
+
- Mark old interfaces as deprecated
|
|
211
|
+
- Add migration guides
|
|
212
|
+
- Plan removal in next major version
|
|
213
|
+
|
|
214
|
+
### Phase 4: Architecture Cleanup (Week 4)
|
|
215
|
+
1. **Reorganize file structure**
|
|
216
|
+
```
|
|
217
|
+
ts/core/
|
|
218
|
+
├── routing/
|
|
219
|
+
│ ├── index.ts
|
|
220
|
+
│ ├── types.ts
|
|
221
|
+
│ ├── matchers/
|
|
222
|
+
│ │ ├── domain.ts
|
|
223
|
+
│ │ ├── path.ts
|
|
224
|
+
│ │ ├── ip.ts
|
|
225
|
+
│ │ └── index.ts
|
|
226
|
+
│ ├── route-store.ts
|
|
227
|
+
│ ├── route-matcher.ts
|
|
228
|
+
│ └── route-manager.ts
|
|
229
|
+
└── utils/
|
|
230
|
+
└── (remove route-specific utils)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
2. **Update documentation**
|
|
234
|
+
- Architecture diagrams
|
|
235
|
+
- Migration guides
|
|
236
|
+
- API documentation
|
|
237
|
+
|
|
238
|
+
3. **Performance optimization**
|
|
239
|
+
- Add caching where beneficial
|
|
240
|
+
- Optimize hot paths
|
|
241
|
+
- Benchmark before/after
|
|
242
|
+
|
|
243
|
+
## Migration Strategy
|
|
244
|
+
|
|
245
|
+
### For SmartProxy RouteManager Users
|
|
246
|
+
```typescript
|
|
247
|
+
// Old way
|
|
248
|
+
import { RouteManager } from './route-manager.js';
|
|
249
|
+
const manager = new RouteManager(options);
|
|
250
|
+
|
|
251
|
+
// New way
|
|
252
|
+
import { SharedRouteManager as RouteManager } from '../core/utils/route-manager.js';
|
|
253
|
+
const manager = new RouteManager({
|
|
254
|
+
logger: options.logger,
|
|
255
|
+
enableDetailedLogging: options.enableDetailedLogging
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### For Router Users
|
|
260
|
+
```typescript
|
|
261
|
+
// Old way
|
|
262
|
+
const proxyRouter = new ProxyRouter();
|
|
263
|
+
const routeRouter = new RouteRouter();
|
|
264
|
+
|
|
265
|
+
// New way
|
|
266
|
+
const router = new HttpRouter(routeManager);
|
|
267
|
+
// Automatically handles both modern and legacy configs
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Success Metrics
|
|
271
|
+
|
|
272
|
+
1. **Code Reduction**
|
|
273
|
+
- Target: Remove ~1,500 lines of duplicate code
|
|
274
|
+
- Measure: Lines of code before/after
|
|
275
|
+
|
|
276
|
+
2. **Performance**
|
|
277
|
+
- Target: No regression in routing performance
|
|
278
|
+
- Measure: Benchmark route matching operations
|
|
279
|
+
|
|
280
|
+
3. **Maintainability**
|
|
281
|
+
- Target: Single implementation for each concept
|
|
282
|
+
- Measure: Time to implement new features
|
|
283
|
+
|
|
284
|
+
4. **Test Coverage**
|
|
285
|
+
- Target: 100% coverage of routing logic
|
|
286
|
+
- Measure: Coverage reports
|
|
287
|
+
|
|
288
|
+
## Risks and Mitigations
|
|
289
|
+
|
|
290
|
+
### Risk 1: Breaking Changes
|
|
291
|
+
- **Mitigation**: Extensive adapter patterns and backward compatibility layers
|
|
292
|
+
- **Testing**: Run all existing tests plus new integration tests
|
|
293
|
+
|
|
294
|
+
### Risk 2: Performance Regression
|
|
295
|
+
- **Mitigation**: Benchmark critical paths before changes
|
|
296
|
+
- **Testing**: Load testing with production-like scenarios
|
|
297
|
+
|
|
298
|
+
### Risk 3: Hidden Dependencies
|
|
299
|
+
- **Mitigation**: Careful code analysis and dependency mapping
|
|
300
|
+
- **Testing**: Integration tests across all proxy types
|
|
301
|
+
|
|
302
|
+
## Long-term Vision
|
|
303
|
+
|
|
304
|
+
### Future Enhancements
|
|
305
|
+
1. **Route Caching**: LRU cache for frequently accessed routes
|
|
306
|
+
2. **Route Indexing**: Trie-based indexing for faster domain matching
|
|
307
|
+
3. **Route Priorities**: Explicit priority system instead of specificity
|
|
308
|
+
4. **Dynamic Routes**: Support for runtime route modifications
|
|
309
|
+
5. **Route Templates**: Reusable route configurations
|
|
310
|
+
|
|
311
|
+
### API Evolution
|
|
312
|
+
```typescript
|
|
313
|
+
// Future unified routing API
|
|
314
|
+
const routingEngine = new RoutingEngine({
|
|
315
|
+
stores: [fileStore, dbStore, dynamicStore],
|
|
316
|
+
matchers: [domainMatcher, pathMatcher, customMatcher],
|
|
317
|
+
cache: new LRUCache({ max: 1000 }),
|
|
318
|
+
indexes: {
|
|
319
|
+
domain: new TrieIndex(),
|
|
320
|
+
path: new RadixTree()
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Simple, powerful API
|
|
325
|
+
const route = await routingEngine.findRoute({
|
|
326
|
+
domain: 'example.com',
|
|
327
|
+
path: '/api/v1/users',
|
|
328
|
+
ip: '192.168.1.1',
|
|
329
|
+
headers: { 'x-custom': 'value' }
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Conclusion
|
|
334
|
+
|
|
335
|
+
The current routing architecture has significant duplication and inconsistencies. By following this unification plan, we can:
|
|
336
|
+
1. Reduce code by ~30%
|
|
337
|
+
2. Improve maintainability
|
|
338
|
+
3. Ensure consistent behavior
|
|
339
|
+
4. Enable future enhancements
|
|
340
|
+
|
|
341
|
+
The phased approach minimizes risk while delivering incremental value. Each phase is independently valuable and can be deployed separately.
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '19.5.
|
|
6
|
+
version: '19.5.19',
|
|
7
7
|
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.'
|
|
8
8
|
}
|
package/ts/core/models/index.ts
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as net from 'net';
|
|
2
|
+
import { WrappedSocket } from './wrapped-socket.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Type guard to check if a socket is a WrappedSocket
|
|
6
|
+
*/
|
|
7
|
+
export function isWrappedSocket(socket: net.Socket | WrappedSocket): socket is WrappedSocket {
|
|
8
|
+
return socket instanceof WrappedSocket || 'socket' in socket;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Helper to get the underlying socket from either a Socket or WrappedSocket
|
|
13
|
+
*/
|
|
14
|
+
export function getUnderlyingSocket(socket: net.Socket | WrappedSocket): net.Socket {
|
|
15
|
+
return isWrappedSocket(socket) ? socket.socket : socket;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Type that represents either a regular socket or a wrapped socket
|
|
20
|
+
*/
|
|
21
|
+
export type AnySocket = net.Socket | WrappedSocket;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WrappedSocket wraps a regular net.Socket to provide transparent access
|
|
5
|
+
* to the real client IP and port when behind a proxy using PROXY protocol.
|
|
6
|
+
*
|
|
7
|
+
* This is the FOUNDATION for all PROXY protocol support and must be implemented
|
|
8
|
+
* before any protocol parsing can occur.
|
|
9
|
+
*
|
|
10
|
+
* This implementation uses a Proxy to delegate all properties and methods
|
|
11
|
+
* to the underlying socket while allowing override of specific properties.
|
|
12
|
+
*/
|
|
13
|
+
export class WrappedSocket {
|
|
14
|
+
public readonly socket: plugins.net.Socket;
|
|
15
|
+
private realClientIP?: string;
|
|
16
|
+
private realClientPort?: number;
|
|
17
|
+
|
|
18
|
+
// Make TypeScript happy by declaring the Socket methods that will be proxied
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
socket: plugins.net.Socket,
|
|
23
|
+
realClientIP?: string,
|
|
24
|
+
realClientPort?: number
|
|
25
|
+
) {
|
|
26
|
+
this.socket = socket;
|
|
27
|
+
this.realClientIP = realClientIP;
|
|
28
|
+
this.realClientPort = realClientPort;
|
|
29
|
+
|
|
30
|
+
// Create a proxy that delegates everything to the underlying socket
|
|
31
|
+
return new Proxy(this, {
|
|
32
|
+
get(target, prop, receiver) {
|
|
33
|
+
// Override specific properties
|
|
34
|
+
if (prop === 'remoteAddress') {
|
|
35
|
+
return target.remoteAddress;
|
|
36
|
+
}
|
|
37
|
+
if (prop === 'remotePort') {
|
|
38
|
+
return target.remotePort;
|
|
39
|
+
}
|
|
40
|
+
if (prop === 'socket') {
|
|
41
|
+
return target.socket;
|
|
42
|
+
}
|
|
43
|
+
if (prop === 'realClientIP') {
|
|
44
|
+
return target.realClientIP;
|
|
45
|
+
}
|
|
46
|
+
if (prop === 'realClientPort') {
|
|
47
|
+
return target.realClientPort;
|
|
48
|
+
}
|
|
49
|
+
if (prop === 'isFromTrustedProxy') {
|
|
50
|
+
return target.isFromTrustedProxy;
|
|
51
|
+
}
|
|
52
|
+
if (prop === 'setProxyInfo') {
|
|
53
|
+
return target.setProxyInfo.bind(target);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// For all other properties/methods, delegate to the underlying socket
|
|
57
|
+
const value = target.socket[prop as keyof plugins.net.Socket];
|
|
58
|
+
if (typeof value === 'function') {
|
|
59
|
+
return value.bind(target.socket);
|
|
60
|
+
}
|
|
61
|
+
return value;
|
|
62
|
+
},
|
|
63
|
+
set(target, prop, value) {
|
|
64
|
+
// Set on the underlying socket
|
|
65
|
+
(target.socket as any)[prop] = value;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}) as any;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns the real client IP if available, otherwise the socket's remote address
|
|
73
|
+
*/
|
|
74
|
+
get remoteAddress(): string | undefined {
|
|
75
|
+
return this.realClientIP || this.socket.remoteAddress;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Returns the real client port if available, otherwise the socket's remote port
|
|
80
|
+
*/
|
|
81
|
+
get remotePort(): number | undefined {
|
|
82
|
+
return this.realClientPort || this.socket.remotePort;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Indicates if this connection came through a trusted proxy
|
|
87
|
+
*/
|
|
88
|
+
get isFromTrustedProxy(): boolean {
|
|
89
|
+
return !!this.realClientIP;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Updates the real client information (called after parsing PROXY protocol)
|
|
94
|
+
*/
|
|
95
|
+
setProxyInfo(ip: string, port: number): void {
|
|
96
|
+
this.realClientIP = ip;
|
|
97
|
+
this.realClientPort = port;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified routing module
|
|
3
|
+
* Provides all routing functionality in a centralized location
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Export all types
|
|
7
|
+
export * from './types.js';
|
|
8
|
+
|
|
9
|
+
// Export all matchers
|
|
10
|
+
export * from './matchers/index.js';
|
|
11
|
+
|
|
12
|
+
// Export specificity calculator
|
|
13
|
+
export * from './specificity.js';
|
|
14
|
+
|
|
15
|
+
// Export route management
|
|
16
|
+
export * from './route-manager.js';
|
|
17
|
+
export * from './route-utils.js';
|
|
18
|
+
|
|
19
|
+
// Convenience re-exports
|
|
20
|
+
export { matchers } from './matchers/index.js';
|
|
21
|
+
export { RouteSpecificity } from './specificity.js';
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { IMatcher, IDomainMatchOptions } from '../types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DomainMatcher provides comprehensive domain matching functionality
|
|
5
|
+
* Supporting exact matches, wildcards, and case-insensitive matching
|
|
6
|
+
*/
|
|
7
|
+
export class DomainMatcher implements IMatcher<boolean, IDomainMatchOptions> {
|
|
8
|
+
private static wildcardToRegex(pattern: string): RegExp {
|
|
9
|
+
// Escape special regex characters except *
|
|
10
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
11
|
+
// Replace * with regex equivalent
|
|
12
|
+
const regexPattern = escaped.replace(/\*/g, '.*');
|
|
13
|
+
return new RegExp(`^${regexPattern}$`, 'i');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Match a domain pattern against a hostname
|
|
18
|
+
* @param pattern The pattern to match (supports wildcards like *.example.com)
|
|
19
|
+
* @param hostname The hostname to test
|
|
20
|
+
* @param options Matching options
|
|
21
|
+
* @returns true if the hostname matches the pattern
|
|
22
|
+
*/
|
|
23
|
+
static match(
|
|
24
|
+
pattern: string,
|
|
25
|
+
hostname: string,
|
|
26
|
+
options: IDomainMatchOptions = {}
|
|
27
|
+
): boolean {
|
|
28
|
+
// Handle null/undefined cases
|
|
29
|
+
if (!pattern || !hostname) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Normalize inputs
|
|
34
|
+
const normalizedPattern = pattern.toLowerCase().trim();
|
|
35
|
+
const normalizedHostname = hostname.toLowerCase().trim();
|
|
36
|
+
|
|
37
|
+
// Remove trailing dots (FQDN normalization)
|
|
38
|
+
const cleanPattern = normalizedPattern.replace(/\.$/, '');
|
|
39
|
+
const cleanHostname = normalizedHostname.replace(/\.$/, '');
|
|
40
|
+
|
|
41
|
+
// Exact match (most common case)
|
|
42
|
+
if (cleanPattern === cleanHostname) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Wildcard matching
|
|
47
|
+
if (options.allowWildcards !== false && cleanPattern.includes('*')) {
|
|
48
|
+
const regex = this.wildcardToRegex(cleanPattern);
|
|
49
|
+
return regex.test(cleanHostname);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// No match
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if a pattern contains wildcards
|
|
58
|
+
*/
|
|
59
|
+
static isWildcardPattern(pattern: string): boolean {
|
|
60
|
+
return pattern.includes('*');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Calculate the specificity of a domain pattern
|
|
65
|
+
* Higher values mean more specific patterns
|
|
66
|
+
*/
|
|
67
|
+
static calculateSpecificity(pattern: string): number {
|
|
68
|
+
if (!pattern) return 0;
|
|
69
|
+
|
|
70
|
+
let score = 0;
|
|
71
|
+
|
|
72
|
+
// Exact domains are most specific
|
|
73
|
+
if (!pattern.includes('*')) {
|
|
74
|
+
score += 100;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Count domain segments
|
|
78
|
+
const segments = pattern.split('.');
|
|
79
|
+
score += segments.length * 10;
|
|
80
|
+
|
|
81
|
+
// Penalize wildcards based on position
|
|
82
|
+
if (pattern.startsWith('*')) {
|
|
83
|
+
score -= 50; // Leading wildcard is very generic
|
|
84
|
+
} else if (pattern.includes('*')) {
|
|
85
|
+
score -= 20; // Wildcard elsewhere is less generic
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Bonus for longer patterns
|
|
89
|
+
score += pattern.length;
|
|
90
|
+
|
|
91
|
+
return score;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Find all matching patterns from a list
|
|
96
|
+
* Returns patterns sorted by specificity (most specific first)
|
|
97
|
+
*/
|
|
98
|
+
static findAllMatches(
|
|
99
|
+
patterns: string[],
|
|
100
|
+
hostname: string,
|
|
101
|
+
options: IDomainMatchOptions = {}
|
|
102
|
+
): string[] {
|
|
103
|
+
const matches = patterns.filter(pattern =>
|
|
104
|
+
this.match(pattern, hostname, options)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// Sort by specificity (highest first)
|
|
108
|
+
return matches.sort((a, b) =>
|
|
109
|
+
this.calculateSpecificity(b) - this.calculateSpecificity(a)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Instance method for interface compliance
|
|
115
|
+
*/
|
|
116
|
+
match(pattern: string, hostname: string, options?: IDomainMatchOptions): boolean {
|
|
117
|
+
return DomainMatcher.match(pattern, hostname, options);
|
|
118
|
+
}
|
|
119
|
+
}
|