update
This commit is contained in:
@@ -10,7 +10,7 @@ import { ConnectionPool } from './connection-pool.js';
|
||||
import { ContextCreator } from './context-creator.js';
|
||||
import { HttpRequestHandler } from './http-request-handler.js';
|
||||
import { Http2RequestHandler } from './http2-request-handler.js';
|
||||
import type { IRouteConfig } from '../smart-proxy/models/route-types.js';
|
||||
import type { IRouteConfig, IRouteTarget } from '../smart-proxy/models/route-types.js';
|
||||
import type { IRouteContext, IHttpRouteContext } from '../../core/models/route-context.js';
|
||||
import { toBaseContext } from '../../core/models/route-context.js';
|
||||
import { TemplateUtils } from '../../core/utils/template-utils.js';
|
||||
@@ -99,6 +99,80 @@ export class RequestHandler {
|
||||
return { ...this.defaultHeaders };
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the appropriate target from the targets array based on sub-matching criteria
|
||||
*/
|
||||
private selectTarget(
|
||||
targets: IRouteTarget[],
|
||||
context: {
|
||||
port: number;
|
||||
path?: string;
|
||||
headers?: Record<string, string>;
|
||||
method?: string;
|
||||
}
|
||||
): IRouteTarget | null {
|
||||
// Sort targets by priority (higher first)
|
||||
const sortedTargets = [...targets].sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
||||
|
||||
// Find the first matching target
|
||||
for (const target of sortedTargets) {
|
||||
if (!target.match) {
|
||||
// No match criteria means this is a default/fallback target
|
||||
return target;
|
||||
}
|
||||
|
||||
// Check port match
|
||||
if (target.match.ports && !target.match.ports.includes(context.port)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check path match (supports wildcards)
|
||||
if (target.match.path && context.path) {
|
||||
const pathPattern = target.match.path.replace(/\*/g, '.*');
|
||||
const pathRegex = new RegExp(`^${pathPattern}$`);
|
||||
if (!pathRegex.test(context.path)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check method match
|
||||
if (target.match.method && context.method && !target.match.method.includes(context.method)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check headers match
|
||||
if (target.match.headers && context.headers) {
|
||||
let headersMatch = true;
|
||||
for (const [key, pattern] of Object.entries(target.match.headers)) {
|
||||
const headerValue = context.headers[key.toLowerCase()];
|
||||
if (!headerValue) {
|
||||
headersMatch = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pattern instanceof RegExp) {
|
||||
if (!pattern.test(headerValue)) {
|
||||
headersMatch = false;
|
||||
break;
|
||||
}
|
||||
} else if (headerValue !== pattern) {
|
||||
headersMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!headersMatch) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// All criteria matched
|
||||
return target;
|
||||
}
|
||||
|
||||
// No matching target found, return the first target without match criteria (default)
|
||||
return sortedTargets.find(t => !t.match) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply CORS headers to response if configured
|
||||
* Implements Phase 5.5: Context-aware CORS handling
|
||||
@@ -480,17 +554,31 @@ export class RequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a matching route with function-based targets, use it
|
||||
if (matchingRoute && matchingRoute.action.type === 'forward' && matchingRoute.action.target) {
|
||||
// If we found a matching route with forward action, select appropriate target
|
||||
if (matchingRoute && matchingRoute.action.type === 'forward' && matchingRoute.action.targets && matchingRoute.action.targets.length > 0) {
|
||||
this.logger.debug(`Found matching route: ${matchingRoute.name || 'unnamed'}`);
|
||||
|
||||
// Select the appropriate target from the targets array
|
||||
const selectedTarget = this.selectTarget(matchingRoute.action.targets, {
|
||||
port: routeContext.port,
|
||||
path: routeContext.path,
|
||||
headers: routeContext.headers,
|
||||
method: routeContext.method
|
||||
});
|
||||
|
||||
if (!selectedTarget) {
|
||||
this.logger.error(`No matching target found for route ${matchingRoute.name}`);
|
||||
req.socket.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract target information, resolving functions if needed
|
||||
let targetHost: string | string[];
|
||||
let targetPort: number;
|
||||
|
||||
try {
|
||||
// Check function cache for host and resolve or use cached value
|
||||
if (typeof matchingRoute.action.target.host === 'function') {
|
||||
if (typeof selectedTarget.host === 'function') {
|
||||
// Generate a function ID for caching (use route name or ID if available)
|
||||
const functionId = `host-${matchingRoute.id || matchingRoute.name || 'unnamed'}`;
|
||||
|
||||
@@ -502,7 +590,7 @@ export class RequestHandler {
|
||||
this.logger.debug(`Using cached host value for ${functionId}`);
|
||||
} else {
|
||||
// Resolve the function and cache the result
|
||||
const resolvedHost = matchingRoute.action.target.host(toBaseContext(routeContext));
|
||||
const resolvedHost = selectedTarget.host(toBaseContext(routeContext));
|
||||
targetHost = resolvedHost;
|
||||
|
||||
// Cache the result
|
||||
@@ -511,16 +599,16 @@ export class RequestHandler {
|
||||
}
|
||||
} else {
|
||||
// No cache available, just resolve
|
||||
const resolvedHost = matchingRoute.action.target.host(routeContext);
|
||||
const resolvedHost = selectedTarget.host(routeContext);
|
||||
targetHost = resolvedHost;
|
||||
this.logger.debug(`Resolved function-based host to: ${Array.isArray(resolvedHost) ? resolvedHost.join(', ') : resolvedHost}`);
|
||||
}
|
||||
} else {
|
||||
targetHost = matchingRoute.action.target.host;
|
||||
targetHost = selectedTarget.host;
|
||||
}
|
||||
|
||||
// Check function cache for port and resolve or use cached value
|
||||
if (typeof matchingRoute.action.target.port === 'function') {
|
||||
if (typeof selectedTarget.port === 'function') {
|
||||
// Generate a function ID for caching
|
||||
const functionId = `port-${matchingRoute.id || matchingRoute.name || 'unnamed'}`;
|
||||
|
||||
@@ -532,7 +620,7 @@ export class RequestHandler {
|
||||
this.logger.debug(`Using cached port value for ${functionId}`);
|
||||
} else {
|
||||
// Resolve the function and cache the result
|
||||
const resolvedPort = matchingRoute.action.target.port(toBaseContext(routeContext));
|
||||
const resolvedPort = selectedTarget.port(toBaseContext(routeContext));
|
||||
targetPort = resolvedPort;
|
||||
|
||||
// Cache the result
|
||||
@@ -541,12 +629,12 @@ export class RequestHandler {
|
||||
}
|
||||
} else {
|
||||
// No cache available, just resolve
|
||||
const resolvedPort = matchingRoute.action.target.port(routeContext);
|
||||
const resolvedPort = selectedTarget.port(routeContext);
|
||||
targetPort = resolvedPort;
|
||||
this.logger.debug(`Resolved function-based port to: ${resolvedPort}`);
|
||||
}
|
||||
} else {
|
||||
targetPort = matchingRoute.action.target.port === 'preserve' ? routeContext.port : matchingRoute.action.target.port as number;
|
||||
targetPort = selectedTarget.port === 'preserve' ? routeContext.port : selectedTarget.port as number;
|
||||
}
|
||||
|
||||
// Select a single host if an array was provided
|
||||
@@ -626,17 +714,32 @@ export class RequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a matching route with function-based targets, use it
|
||||
if (matchingRoute && matchingRoute.action.type === 'forward' && matchingRoute.action.target) {
|
||||
// If we found a matching route with forward action, select appropriate target
|
||||
if (matchingRoute && matchingRoute.action.type === 'forward' && matchingRoute.action.targets && matchingRoute.action.targets.length > 0) {
|
||||
this.logger.debug(`Found matching route for HTTP/2 request: ${matchingRoute.name || 'unnamed'}`);
|
||||
|
||||
// Select the appropriate target from the targets array
|
||||
const selectedTarget = this.selectTarget(matchingRoute.action.targets, {
|
||||
port: routeContext.port,
|
||||
path: routeContext.path,
|
||||
headers: routeContext.headers,
|
||||
method: routeContext.method
|
||||
});
|
||||
|
||||
if (!selectedTarget) {
|
||||
this.logger.error(`No matching target found for route ${matchingRoute.name}`);
|
||||
stream.respond({ ':status': 502 });
|
||||
stream.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract target information, resolving functions if needed
|
||||
let targetHost: string | string[];
|
||||
let targetPort: number;
|
||||
|
||||
try {
|
||||
// Check function cache for host and resolve or use cached value
|
||||
if (typeof matchingRoute.action.target.host === 'function') {
|
||||
if (typeof selectedTarget.host === 'function') {
|
||||
// Generate a function ID for caching (use route name or ID if available)
|
||||
const functionId = `host-http2-${matchingRoute.id || matchingRoute.name || 'unnamed'}`;
|
||||
|
||||
@@ -648,7 +751,7 @@ export class RequestHandler {
|
||||
this.logger.debug(`Using cached host value for HTTP/2: ${functionId}`);
|
||||
} else {
|
||||
// Resolve the function and cache the result
|
||||
const resolvedHost = matchingRoute.action.target.host(toBaseContext(routeContext));
|
||||
const resolvedHost = selectedTarget.host(toBaseContext(routeContext));
|
||||
targetHost = resolvedHost;
|
||||
|
||||
// Cache the result
|
||||
@@ -657,16 +760,16 @@ export class RequestHandler {
|
||||
}
|
||||
} else {
|
||||
// No cache available, just resolve
|
||||
const resolvedHost = matchingRoute.action.target.host(routeContext);
|
||||
const resolvedHost = selectedTarget.host(routeContext);
|
||||
targetHost = resolvedHost;
|
||||
this.logger.debug(`Resolved HTTP/2 function-based host to: ${Array.isArray(resolvedHost) ? resolvedHost.join(', ') : resolvedHost}`);
|
||||
}
|
||||
} else {
|
||||
targetHost = matchingRoute.action.target.host;
|
||||
targetHost = selectedTarget.host;
|
||||
}
|
||||
|
||||
// Check function cache for port and resolve or use cached value
|
||||
if (typeof matchingRoute.action.target.port === 'function') {
|
||||
if (typeof selectedTarget.port === 'function') {
|
||||
// Generate a function ID for caching
|
||||
const functionId = `port-http2-${matchingRoute.id || matchingRoute.name || 'unnamed'}`;
|
||||
|
||||
@@ -678,7 +781,7 @@ export class RequestHandler {
|
||||
this.logger.debug(`Using cached port value for HTTP/2: ${functionId}`);
|
||||
} else {
|
||||
// Resolve the function and cache the result
|
||||
const resolvedPort = matchingRoute.action.target.port(toBaseContext(routeContext));
|
||||
const resolvedPort = selectedTarget.port(toBaseContext(routeContext));
|
||||
targetPort = resolvedPort;
|
||||
|
||||
// Cache the result
|
||||
@@ -687,12 +790,12 @@ export class RequestHandler {
|
||||
}
|
||||
} else {
|
||||
// No cache available, just resolve
|
||||
const resolvedPort = matchingRoute.action.target.port(routeContext);
|
||||
const resolvedPort = selectedTarget.port(routeContext);
|
||||
targetPort = resolvedPort;
|
||||
this.logger.debug(`Resolved HTTP/2 function-based port to: ${resolvedPort}`);
|
||||
}
|
||||
} else {
|
||||
targetPort = matchingRoute.action.target.port === 'preserve' ? routeContext.port : matchingRoute.action.target.port as number;
|
||||
targetPort = selectedTarget.port === 'preserve' ? routeContext.port : selectedTarget.port as number;
|
||||
}
|
||||
|
||||
// Select a single host if an array was provided
|
||||
|
Reference in New Issue
Block a user