smartproxy/readme.routing.md

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
  • 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
  • route-patterns.ts: Constants for route patterns
  • route-validators.ts: Route configuration validation
  • route-helpers.ts: Additional utilities
  • route-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)

  1. Audit all matching implementations

    • Document differences in behavior
    • Identify the most comprehensive implementation
    • Create test suite covering all edge cases
  2. 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
    }
    
  3. Update all components to use unified matchers

    • Replace local implementations
    • Ensure backward compatibility
    • Run comprehensive tests

Phase 2: Unify Route Managers (Week 2)

  1. Enhance SharedRouteManager

    • Add any missing features from SmartProxy RouteManager
    • Make it truly generic (no proxy-specific dependencies)
    • Add adapter pattern for different options types
  2. 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
    });
    
  3. Remove duplicate RouteManager

    • Delete ts/proxies/smart-proxy/route-manager.ts
    • Update all imports
    • Verify all tests pass

Phase 3: Consolidate Routers (Week 3)

  1. Create unified HttpRouter

    export class HttpRouter {
      constructor(private routeManager: SharedRouteManager) {}
    
      // Modern interface
      route(req: IncomingMessage): RouteResult
    
      // Legacy adapter
      routeLegacy(config: IReverseProxyConfig): RouteResult
    }
    
  2. Migrate HttpProxy

    • Replace both ProxyRouter and RouteRouter
    • Use single HttpRouter with appropriate adapter
    • Maintain backward compatibility
  3. Clean up legacy code

    • Mark old interfaces as deprecated
    • Add migration guides
    • Plan removal in next major version

Phase 4: Architecture Cleanup (Week 4)

  1. 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)
    
  2. Update documentation

    • Architecture diagrams
    • Migration guides
    • API documentation
  3. 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

  1. Code Reduction

    • Target: Remove ~1,500 lines of duplicate code
    • Measure: Lines of code before/after
  2. Performance

    • Target: No regression in routing performance
    • Measure: Benchmark route matching operations
  3. Maintainability

    • Target: Single implementation for each concept
    • Measure: Time to implement new features
  4. 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

  1. Route Caching: LRU cache for frequently accessed routes
  2. Route Indexing: Trie-based indexing for faster domain matching
  3. Route Priorities: Explicit priority system instead of specificity
  4. Dynamic Routes: Support for runtime route modifications
  5. 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:

  1. Reduce code by ~30%
  2. Improve maintainability
  3. Ensure consistent behavior
  4. Enable future enhancements

The phased approach minimizes risk while delivering incremental value. Each phase is independently valuable and can be deployed separately.