Refactor routing and proxy components for improved structure and compatibility

- Removed deprecated route utility functions in favor of direct matcher usage.
- Updated imports to reflect new module structure for routing utilities.
- Consolidated route manager functionality into SharedRouteManager for better consistency.
- Eliminated legacy routing methods and interfaces, streamlining the HttpProxy and associated components.
- Enhanced WebSocket and HTTP request handling to utilize the new unified HttpRouter.
- Updated route matching logic to leverage matcher classes for domain, path, and header checks.
- Cleaned up legacy compatibility code across various modules, ensuring a more maintainable codebase.
This commit is contained in:
Juergen Kunz
2025-06-03 16:21:09 +00:00
parent cf70b6ace5
commit 2a75e7c490
21 changed files with 148 additions and 881 deletions

View File

@ -4,11 +4,9 @@ import {
type IHttpProxyOptions,
type ILogger,
createLogger,
type IReverseProxyConfig,
RouteManager
} from './models/types.js';
import { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
import { ConnectionPool } from './connection-pool.js';
import { ProxyRouter } from '../../routing/router/index.js';
import { ContextCreator } from './context-creator.js';
import { HttpRequestHandler } from './http-request-handler.js';
import { Http2RequestHandler } from './http2-request-handler.js';
@ -48,10 +46,9 @@ export class RequestHandler {
constructor(
private options: IHttpProxyOptions,
private connectionPool: ConnectionPool,
private legacyRouter: ProxyRouter, // Legacy router for backward compatibility
private routeManager?: RouteManager,
private functionCache?: any, // FunctionCache - using any to avoid circular dependency
private router?: any // RouteRouter - using any to avoid circular dependency
private router?: any // HttpRouter - using any to avoid circular dependency
) {
this.logger = createLogger(options.logLevel || 'info');
this.securityManager = new SecurityManager(this.logger);
@ -373,7 +370,8 @@ export class RequestHandler {
tlsVersion: req.socket.getTLSVersion?.() || undefined
});
matchingRoute = this.routeManager.findMatchingRoute(toBaseContext(routeContext));
const matchResult = this.routeManager.findMatchingRoute(toBaseContext(routeContext));
matchingRoute = matchResult?.route || null;
} catch (err) {
this.logger.error('Error finding matching route', err);
}
@ -581,86 +579,11 @@ export class RequestHandler {
}
}
// Try modern router first, then fall back to legacy routing if needed
if (this.router) {
try {
// Try to find a matching route using the modern router
const route = this.router.routeReq(req);
if (route && route.action.type === 'forward' && route.action.target) {
// Handle this route similarly to RouteManager logic
this.logger.debug(`Found matching route via modern router: ${route.name || 'unnamed'}`);
// No need to do anything here, we'll continue with legacy routing
// The routeManager would have already found this route if applicable
}
} catch (err) {
this.logger.error('Error using modern router', err);
// Continue with legacy routing
}
}
// Fall back to legacy routing if no matching route found via RouteManager
let proxyConfig: IReverseProxyConfig | undefined;
try {
proxyConfig = (this.legacyRouter as any).routeReqLegacy(req);
} catch (err) {
this.logger.error('Error routing request with legacy router', err);
res.statusCode = 500;
res.end('Internal Server Error');
if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
return;
}
if (!proxyConfig) {
this.logger.warn(`No proxy configuration for host: ${req.headers.host}`);
res.statusCode = 404;
res.end('Not Found: No proxy configuration for this host');
if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
return;
}
// Determine protocol to backend (per-domain override or global)
const backendProto = proxyConfig.backendProtocol || this.options.backendProtocol;
if (backendProto === 'http2') {
const destination = this.connectionPool.getNextTarget(
proxyConfig.destinationIps,
proxyConfig.destinationPorts[0]
);
const key = `${destination.host}:${destination.port}`;
let session = this.h2Sessions.get(key);
if (!session || session.closed || (session as any).destroyed) {
session = plugins.http2.connect(`http://${destination.host}:${destination.port}`);
this.h2Sessions.set(key, session);
session.on('error', () => this.h2Sessions.delete(key));
session.on('close', () => this.h2Sessions.delete(key));
}
// Build headers for HTTP/2 request
const hdrs: Record<string, any> = {
':method': req.method,
':path': req.url,
':authority': `${destination.host}:${destination.port}`
};
for (const [hk, hv] of Object.entries(req.headers)) {
if (typeof hv === 'string') hdrs[hk] = hv;
}
const h2Stream = session.request(hdrs);
req.pipe(h2Stream);
h2Stream.on('response', (hdrs2: any) => {
const status = (hdrs2[':status'] as number) || 502;
res.statusCode = status;
// Copy headers from HTTP/2 response to HTTP/1 response
for (const [hk, hv] of Object.entries(hdrs2)) {
if (!hk.startsWith(':') && hv != null) {
res.setHeader(hk, hv as string | string[]);
}
}
h2Stream.pipe(res);
});
h2Stream.on('error', (err) => {
res.statusCode = 502;
res.end(`Bad Gateway: ${err.message}`);
if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
});
return;
}
// If no route was found, return 404
this.logger.warn(`No route configuration for host: ${req.headers.host}`);
res.statusCode = 404;
res.end('Not Found: No route configuration for this host');
if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
}
/**
@ -688,7 +611,8 @@ export class RequestHandler {
let matchingRoute: IRouteConfig | null = null;
if (this.routeManager) {
try {
matchingRoute = this.routeManager.findMatchingRoute(toBaseContext(routeContext));
const matchResult = this.routeManager.findMatchingRoute(toBaseContext(routeContext));
matchingRoute = matchResult?.route || null;
} catch (err) {
this.logger.error('Error finding matching route for HTTP/2 request', err);
}
@ -812,104 +736,9 @@ export class RequestHandler {
const method = headers[':method'] || 'GET';
const path = headers[':path'] || '/';
// If configured to proxy to backends over HTTP/2, use HTTP/2 client sessions
if (this.options.backendProtocol === 'http2') {
const authority = headers[':authority'] as string || '';
const host = authority.split(':')[0];
const fakeReq: any = {
headers: { host },
method: headers[':method'],
url: headers[':path'],
socket: (stream.session as any).socket
};
// Try modern router first if available
let route;
if (this.router) {
try {
route = this.router.routeReq(fakeReq);
if (route && route.action.type === 'forward' && route.action.target) {
this.logger.debug(`Found matching HTTP/2 route via modern router: ${route.name || 'unnamed'}`);
// The routeManager would have already found this route if applicable
}
} catch (err) {
this.logger.error('Error using modern router for HTTP/2', err);
}
}
// Fall back to legacy routing
const proxyConfig = (this.legacyRouter as any).routeReqLegacy(fakeReq);
if (!proxyConfig) {
stream.respond({ ':status': 404 });
stream.end('Not Found');
if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
return;
}
const destination = this.connectionPool.getNextTarget(proxyConfig.destinationIps, proxyConfig.destinationPorts[0]);
// Use the helper for HTTP/2 to HTTP/2 routing
return Http2RequestHandler.handleHttp2WithHttp2Destination(
stream,
headers,
destination,
routeContext,
this.h2Sessions,
this.logger,
this.metricsTracker
);
}
try {
// Determine host for routing
const authority = headers[':authority'] as string || '';
const host = authority.split(':')[0];
// Fake request object for routing
const fakeReq: any = {
headers: { host },
method,
url: path,
socket: (stream.session as any).socket
};
// Try modern router first if available
if (this.router) {
try {
const route = this.router.routeReq(fakeReq);
if (route && route.action.type === 'forward' && route.action.target) {
this.logger.debug(`Found matching HTTP/2 route via modern router: ${route.name || 'unnamed'}`);
// The routeManager would have already found this route if applicable
}
} catch (err) {
this.logger.error('Error using modern router for HTTP/2', err);
}
}
// Fall back to legacy routing
const proxyConfig = (this.legacyRouter as any).routeReqLegacy(fakeReq as any);
if (!proxyConfig) {
stream.respond({ ':status': 404 });
stream.end('Not Found');
if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
return;
}
// Select backend target
const destination = this.connectionPool.getNextTarget(
proxyConfig.destinationIps,
proxyConfig.destinationPorts[0]
);
// Use the helper for HTTP/2 to HTTP/1 routing
return Http2RequestHandler.handleHttp2WithHttp1Destination(
stream,
headers,
destination,
routeContext,
this.logger,
this.metricsTracker
);
} catch (err: any) {
stream.respond({ ':status': 500 });
stream.end('Internal Server Error');
if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
}
// No route was found
stream.respond({ ':status': 404 });
stream.end('Not Found: No route configuration for this request');
if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
}
}