10 KiB
SmartProxy Routing Architecture Unification Plan
Overview
This document analyzes the current state of routing in SmartProxy, identifies redundancies and inconsistencies, and proposes a unified architecture.
Current State Analysis
1. Multiple Route Manager Implementations
1.1 Core SharedRouteManager (ts/core/utils/route-manager.ts
)
- Purpose: Designed as a shared component for SmartProxy and NetworkProxy
- Features:
- Port mapping and expansion (e.g.,
[80, 443]
→ individual routes) - Comprehensive route matching (domain, path, IP, headers, TLS)
- Route validation and conflict detection
- Event emitter for route changes
- Detailed logging support
- Port mapping and expansion (e.g.,
- Status: Well-designed but underutilized
1.2 SmartProxy RouteManager (ts/proxies/smart-proxy/route-manager.ts
)
- Purpose: SmartProxy-specific route management
- Issues:
- 95% duplicate code from SharedRouteManager
- Only difference is using
ISmartProxyOptions
instead of generic interface - Contains deprecated security methods
- Unnecessary code duplication
- Status: Should be removed in favor of SharedRouteManager
1.3 HttpProxy Route Management (ts/proxies/http-proxy/
)
- Purpose: HTTP-specific routing
- Implementation: Minimal, inline route matching
- Status: Could benefit from SharedRouteManager
2. Multiple Router Implementations
2.1 ProxyRouter (ts/routing/router/proxy-router.ts
)
- Purpose: Legacy compatibility with
IReverseProxyConfig
- Features: Domain-based routing with path patterns
- Used by: HttpProxy for backward compatibility
2.2 RouteRouter (ts/routing/router/route-router.ts
)
- Purpose: Modern routing with
IRouteConfig
- Features: Nearly identical to ProxyRouter
- Issues: Code duplication with ProxyRouter
3. Scattered Route Utilities
3.1 Core route-utils (ts/core/utils/route-utils.ts
)
- Purpose: Shared matching functions
- Features: Domain, path, IP, CIDR matching
- Status: Well-implemented, should be the single source
3.2 SmartProxy route-utils (ts/proxies/smart-proxy/utils/route-utils.ts
)
- Purpose: Route configuration utilities
- Features: Different scope - config merging, not pattern matching
- Status: Keep separate as it serves different purpose
4. Other Route-Related Files
route-patterns.ts
: Constants for route patternsroute-validators.ts
: Route configuration validationroute-helpers.ts
: Additional utilitiesroute-connection-handler.ts
: Connection routing logic
Problems Identified
1. Code Duplication
- SharedRouteManager vs SmartProxy RouteManager: ~1000 lines of duplicate code
- ProxyRouter vs RouteRouter: ~500 lines of duplicate code
- Matching logic: Implemented in 4+ different places
2. Inconsistent Implementations
// Example: Domain matching appears in multiple places
// 1. In route-utils.ts
export function matchDomain(pattern: string, hostname: string): boolean
// 2. In SmartProxy RouteManager
private matchDomain(domain: string, hostname: string): boolean
// 3. In ProxyRouter
private matchesHostname(configName: string, hostname: string): boolean
// 4. In RouteRouter
private matchDomain(pattern: string, hostname: string): boolean
3. Unclear Separation of Concerns
- Route Managers handle both storage AND matching
- Routers also handle storage AND matching
- No clear boundaries between layers
4. Maintenance Burden
- Bug fixes need to be applied in multiple places
- New features must be implemented multiple times
- Testing effort multiplied
Proposed Unified Architecture
Layer 1: Core Routing Components
ts/core/routing/
├── types.ts # All route-related types
├── utils.ts # All matching logic (consolidated)
├── route-store.ts # Route storage and indexing
└── route-matcher.ts # Route matching engine
Layer 2: Route Management
ts/core/routing/
└── route-manager.ts # Single RouteManager for all proxies
- Uses RouteStore for storage
- Uses RouteMatcher for matching
- Provides high-level API
Layer 3: HTTP Routing
ts/routing/
└── http-router.ts # Single HTTP router implementation
- Uses RouteManager for route lookup
- Handles HTTP-specific concerns
- Legacy adapter built-in
Layer 4: Proxy Integration
ts/proxies/
├── smart-proxy/
│ └── (uses core RouteManager directly)
├── http-proxy/
│ └── (uses core RouteManager + HttpRouter)
└── network-proxy/
└── (uses core RouteManager directly)
Implementation Plan
Phase 1: Consolidate Matching Logic (Week 1)
-
Audit all matching implementations
- Document differences in behavior
- Identify the most comprehensive implementation
- Create test suite covering all edge cases
-
Create unified matching module
// ts/core/routing/matchers.ts export class DomainMatcher { static match(pattern: string, hostname: string): boolean } export class PathMatcher { static match(pattern: string, path: string): MatchResult } export class IpMatcher { static match(pattern: string, ip: string): boolean static matchCidr(cidr: string, ip: string): boolean }
-
Update all components to use unified matchers
- Replace local implementations
- Ensure backward compatibility
- Run comprehensive tests
Phase 2: Unify Route Managers (Week 2)
-
Enhance SharedRouteManager
- Add any missing features from SmartProxy RouteManager
- Make it truly generic (no proxy-specific dependencies)
- Add adapter pattern for different options types
-
Migrate SmartProxy to use SharedRouteManager
// Before this.routeManager = new RouteManager(this.settings); // After this.routeManager = new SharedRouteManager({ logger: this.settings.logger, enableDetailedLogging: this.settings.enableDetailedLogging });
-
Remove duplicate RouteManager
- Delete
ts/proxies/smart-proxy/route-manager.ts
- Update all imports
- Verify all tests pass
- Delete
Phase 3: Consolidate Routers (Week 3)
-
Create unified HttpRouter
export class HttpRouter { constructor(private routeManager: SharedRouteManager) {} // Modern interface route(req: IncomingMessage): RouteResult // Legacy adapter routeLegacy(config: IReverseProxyConfig): RouteResult }
-
Migrate HttpProxy
- Replace both ProxyRouter and RouteRouter
- Use single HttpRouter with appropriate adapter
- Maintain backward compatibility
-
Clean up legacy code
- Mark old interfaces as deprecated
- Add migration guides
- Plan removal in next major version
Phase 4: Architecture Cleanup (Week 4)
-
Reorganize file structure
ts/core/ ├── routing/ │ ├── index.ts │ ├── types.ts │ ├── matchers/ │ │ ├── domain.ts │ │ ├── path.ts │ │ ├── ip.ts │ │ └── index.ts │ ├── route-store.ts │ ├── route-matcher.ts │ └── route-manager.ts └── utils/ └── (remove route-specific utils)
-
Update documentation
- Architecture diagrams
- Migration guides
- API documentation
-
Performance optimization
- Add caching where beneficial
- Optimize hot paths
- Benchmark before/after
Migration Strategy
For SmartProxy RouteManager Users
// Old way
import { RouteManager } from './route-manager.js';
const manager = new RouteManager(options);
// New way
import { SharedRouteManager as RouteManager } from '../core/utils/route-manager.js';
const manager = new RouteManager({
logger: options.logger,
enableDetailedLogging: options.enableDetailedLogging
});
For Router Users
// Old way
const proxyRouter = new ProxyRouter();
const routeRouter = new RouteRouter();
// New way
const router = new HttpRouter(routeManager);
// Automatically handles both modern and legacy configs
Success Metrics
-
Code Reduction
- Target: Remove ~1,500 lines of duplicate code
- Measure: Lines of code before/after
-
Performance
- Target: No regression in routing performance
- Measure: Benchmark route matching operations
-
Maintainability
- Target: Single implementation for each concept
- Measure: Time to implement new features
-
Test Coverage
- Target: 100% coverage of routing logic
- Measure: Coverage reports
Risks and Mitigations
Risk 1: Breaking Changes
- Mitigation: Extensive adapter patterns and backward compatibility layers
- Testing: Run all existing tests plus new integration tests
Risk 2: Performance Regression
- Mitigation: Benchmark critical paths before changes
- Testing: Load testing with production-like scenarios
Risk 3: Hidden Dependencies
- Mitigation: Careful code analysis and dependency mapping
- Testing: Integration tests across all proxy types
Long-term Vision
Future Enhancements
- Route Caching: LRU cache for frequently accessed routes
- Route Indexing: Trie-based indexing for faster domain matching
- Route Priorities: Explicit priority system instead of specificity
- Dynamic Routes: Support for runtime route modifications
- Route Templates: Reusable route configurations
API Evolution
// Future unified routing API
const routingEngine = new RoutingEngine({
stores: [fileStore, dbStore, dynamicStore],
matchers: [domainMatcher, pathMatcher, customMatcher],
cache: new LRUCache({ max: 1000 }),
indexes: {
domain: new TrieIndex(),
path: new RadixTree()
}
});
// Simple, powerful API
const route = await routingEngine.findRoute({
domain: 'example.com',
path: '/api/v1/users',
ip: '192.168.1.1',
headers: { 'x-custom': 'value' }
});
Conclusion
The current routing architecture has significant duplication and inconsistencies. By following this unification plan, we can:
- Reduce code by ~30%
- Improve maintainability
- Ensure consistent behavior
- Enable future enhancements
The phased approach minimizes risk while delivering incremental value. Each phase is independently valuable and can be deployed separately.