- Introduced a centralized routing module with comprehensive matchers for domains, headers, IPs, and paths. - Added DomainMatcher for domain pattern matching with support for wildcards and specificity calculation. - Implemented HeaderMatcher for HTTP header matching, including exact matches and pattern support. - Developed IpMatcher for IP address matching, supporting CIDR notation, ranges, and wildcards. - Created PathMatcher for path matching with parameter extraction and wildcard support. - Established RouteSpecificity class to calculate and compare route specificity scores. - Enhanced HttpRouter to utilize the new matching system, supporting both modern and legacy route configurations. - Added detailed logging and error handling for routing operations.
142 lines
4.4 KiB
TypeScript
142 lines
4.4 KiB
TypeScript
/**
|
|
* Route matching utilities for SmartProxy components
|
|
*
|
|
* This file provides utility functions that use the unified matchers
|
|
* and additional route-specific utilities.
|
|
*/
|
|
|
|
import { DomainMatcher, PathMatcher, IpMatcher, HeaderMatcher } from '../routing/matchers/index.js';
|
|
import { RouteSpecificity } from '../routing/specificity.js';
|
|
import type { IRouteSpecificity } from '../routing/types.js';
|
|
import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
|
|
|
|
/**
|
|
* Match a domain pattern against a domain
|
|
* @deprecated Use DomainMatcher.match() directly
|
|
*/
|
|
export function matchDomain(pattern: string, domain: string): boolean {
|
|
return DomainMatcher.match(pattern, domain);
|
|
}
|
|
|
|
/**
|
|
* Match domains from a route against a given domain
|
|
*
|
|
* @param domains Array or single domain pattern to match against
|
|
* @param domain Domain to match
|
|
* @returns Whether the domain matches any of the patterns
|
|
*/
|
|
export function matchRouteDomain(domains: string | string[] | undefined, domain: string | undefined): boolean {
|
|
// If no domains specified in the route, match all domains
|
|
if (!domains) {
|
|
return true;
|
|
}
|
|
|
|
// If no domain in the request, can't match domain-specific routes
|
|
if (!domain) {
|
|
return false;
|
|
}
|
|
|
|
const patterns = Array.isArray(domains) ? domains : [domains];
|
|
return patterns.some(pattern => matchDomain(pattern, domain));
|
|
}
|
|
|
|
/**
|
|
* Match a path pattern against a path
|
|
* @deprecated Use PathMatcher.match() directly
|
|
*/
|
|
export function matchPath(pattern: string, path: string): boolean {
|
|
return PathMatcher.match(pattern, path).matches;
|
|
}
|
|
|
|
// Helper functions removed - use IpMatcher internal methods instead
|
|
|
|
/**
|
|
* Match an IP against a CIDR pattern
|
|
* @deprecated Use IpMatcher.matchCidr() directly
|
|
*/
|
|
export function matchIpCidr(cidr: string, ip: string): boolean {
|
|
return IpMatcher.matchCidr(cidr, ip);
|
|
}
|
|
|
|
/**
|
|
* Match an IP pattern against an IP
|
|
* @deprecated Use IpMatcher.match() directly
|
|
*/
|
|
export function matchIpPattern(pattern: string, ip: string): boolean {
|
|
return IpMatcher.match(pattern, ip);
|
|
}
|
|
|
|
/**
|
|
* Match an IP against allowed and blocked IP patterns
|
|
* @deprecated Use IpMatcher.isAuthorized() directly
|
|
*/
|
|
export function isIpAuthorized(
|
|
ip: string,
|
|
ipAllowList: string[] = ['*'],
|
|
ipBlockList: string[] = []
|
|
): boolean {
|
|
return IpMatcher.isAuthorized(ip, ipAllowList, ipBlockList);
|
|
}
|
|
|
|
/**
|
|
* Match an HTTP header pattern against a header value
|
|
* @deprecated Use HeaderMatcher.match() directly
|
|
*/
|
|
export function matchHeader(pattern: string | RegExp, value: string): boolean {
|
|
// Convert RegExp to string pattern for HeaderMatcher
|
|
const stringPattern = pattern instanceof RegExp ? pattern.source : pattern;
|
|
return HeaderMatcher.match(stringPattern, value, { exactMatch: true });
|
|
}
|
|
|
|
/**
|
|
* Calculate route specificity score
|
|
* Higher score means more specific matching criteria
|
|
*
|
|
* @param match Match criteria to evaluate
|
|
* @returns Numeric specificity score
|
|
* @deprecated Consider using RouteSpecificity.calculate() with full IRouteConfig
|
|
*/
|
|
export function calculateRouteSpecificity(match: {
|
|
domains?: string | string[];
|
|
path?: string;
|
|
clientIp?: string[];
|
|
tlsVersion?: string[];
|
|
headers?: Record<string, string | RegExp>;
|
|
}): number {
|
|
let score = 0;
|
|
|
|
// Path specificity using PathMatcher
|
|
if (match.path) {
|
|
score += PathMatcher.calculateSpecificity(match.path);
|
|
}
|
|
|
|
// Domain specificity using DomainMatcher
|
|
if (match.domains) {
|
|
const domains = Array.isArray(match.domains) ? match.domains : [match.domains];
|
|
// Use the highest specificity among all domains
|
|
const domainScore = Math.max(...domains.map(d => DomainMatcher.calculateSpecificity(d)));
|
|
score += domainScore;
|
|
}
|
|
|
|
// Headers specificity using HeaderMatcher
|
|
if (match.headers) {
|
|
const stringHeaders: Record<string, string> = {};
|
|
for (const [key, value] of Object.entries(match.headers)) {
|
|
stringHeaders[key] = value instanceof RegExp ? value.source : value;
|
|
}
|
|
score += HeaderMatcher.calculateSpecificity(stringHeaders);
|
|
}
|
|
|
|
// Client IP adds some specificity
|
|
if (match.clientIp && match.clientIp.length > 0) {
|
|
// Use the first IP pattern for specificity
|
|
score += IpMatcher.calculateSpecificity(match.clientIp[0]);
|
|
}
|
|
|
|
// TLS version adds minimal specificity
|
|
if (match.tlsVersion && match.tlsVersion.length > 0) {
|
|
score += match.tlsVersion.length * 10;
|
|
}
|
|
|
|
return score;
|
|
} |