127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
import * as plugins from '../../plugins.js';
|
|
import type {
|
|
IForwardConfig,
|
|
IForwardingHandler
|
|
} from '../config/forwarding-types.js';
|
|
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
|
|
|
|
/**
|
|
* Base class for all forwarding handlers
|
|
*/
|
|
export abstract class ForwardingHandler extends plugins.EventEmitter implements IForwardingHandler {
|
|
/**
|
|
* Create a new ForwardingHandler
|
|
* @param config The forwarding configuration
|
|
*/
|
|
constructor(protected config: IForwardConfig) {
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* Initialize the handler
|
|
* Base implementation does nothing, subclasses should override as needed
|
|
*/
|
|
public async initialize(): Promise<void> {
|
|
// Base implementation - no initialization needed
|
|
}
|
|
|
|
/**
|
|
* Handle a new socket connection
|
|
* @param socket The incoming socket connection
|
|
*/
|
|
public abstract handleConnection(socket: plugins.net.Socket): void;
|
|
|
|
/**
|
|
* Handle an HTTP request
|
|
* @param req The HTTP request
|
|
* @param res The HTTP response
|
|
*/
|
|
public abstract handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void;
|
|
|
|
/**
|
|
* Get a target from the configuration, supporting round-robin selection
|
|
* @returns A resolved target object with host and port
|
|
*/
|
|
protected getTargetFromConfig(): { host: string, port: number } {
|
|
const { target } = this.config;
|
|
|
|
// Handle round-robin host selection
|
|
if (Array.isArray(target.host)) {
|
|
if (target.host.length === 0) {
|
|
throw new Error('No target hosts specified');
|
|
}
|
|
|
|
// Simple round-robin selection
|
|
const randomIndex = Math.floor(Math.random() * target.host.length);
|
|
return {
|
|
host: target.host[randomIndex],
|
|
port: target.port
|
|
};
|
|
}
|
|
|
|
// Single host
|
|
return {
|
|
host: target.host,
|
|
port: target.port
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Redirect an HTTP request to HTTPS
|
|
* @param req The HTTP request
|
|
* @param res The HTTP response
|
|
*/
|
|
protected redirectToHttps(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void {
|
|
const host = req.headers.host || '';
|
|
const path = req.url || '/';
|
|
const redirectUrl = `https://${host}${path}`;
|
|
|
|
res.writeHead(301, {
|
|
'Location': redirectUrl,
|
|
'Cache-Control': 'no-cache'
|
|
});
|
|
res.end(`Redirecting to ${redirectUrl}`);
|
|
|
|
this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, {
|
|
statusCode: 301,
|
|
headers: { 'Location': redirectUrl },
|
|
size: 0
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Apply custom headers from configuration
|
|
* @param headers The original headers
|
|
* @param variables Variables to replace in the headers
|
|
* @returns The headers with custom values applied
|
|
*/
|
|
protected applyCustomHeaders(
|
|
headers: Record<string, string | string[] | undefined>,
|
|
variables: Record<string, string>
|
|
): Record<string, string | string[] | undefined> {
|
|
const customHeaders = this.config.advanced?.headers || {};
|
|
const result = { ...headers };
|
|
|
|
// Apply custom headers with variable substitution
|
|
for (const [key, value] of Object.entries(customHeaders)) {
|
|
let processedValue = value;
|
|
|
|
// Replace variables in the header value
|
|
for (const [varName, varValue] of Object.entries(variables)) {
|
|
processedValue = processedValue.replace(`{${varName}}`, varValue);
|
|
}
|
|
|
|
result[key] = processedValue;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get the timeout for this connection from configuration
|
|
* @returns Timeout in milliseconds
|
|
*/
|
|
protected getTimeout(): number {
|
|
return this.config.advanced?.timeout || 60000; // Default: 60 seconds
|
|
}
|
|
} |