import type { ForwardConfig } from '../config/forwarding-types.js'; import type { ForwardingHandler } from '../handlers/base-handler.js'; import { HttpForwardingHandler } from '../handlers/http-handler.js'; import { HttpsPassthroughHandler } from '../handlers/https-passthrough-handler.js'; import { HttpsTerminateToHttpHandler } from '../handlers/https-terminate-to-http-handler.js'; import { HttpsTerminateToHttpsHandler } from '../handlers/https-terminate-to-https-handler.js'; /** * Factory for creating forwarding handlers based on the configuration type */ export class ForwardingHandlerFactory { /** * Create a forwarding handler based on the configuration * @param config The forwarding configuration * @returns The appropriate forwarding handler */ public static createHandler(config: ForwardConfig): ForwardingHandler { // Create the appropriate handler based on the forwarding type switch (config.type) { case 'http-only': return new HttpForwardingHandler(config); case 'https-passthrough': return new HttpsPassthroughHandler(config); case 'https-terminate-to-http': return new HttpsTerminateToHttpHandler(config); case 'https-terminate-to-https': return new HttpsTerminateToHttpsHandler(config); default: // Type system should prevent this, but just in case: throw new Error(`Unknown forwarding type: ${(config as any).type}`); } } /** * Apply default values to a forwarding configuration based on its type * @param config The original forwarding configuration * @returns A configuration with defaults applied */ public static applyDefaults(config: ForwardConfig): ForwardConfig { // Create a deep copy of the configuration const result: ForwardConfig = JSON.parse(JSON.stringify(config)); // Apply defaults based on forwarding type switch (config.type) { case 'http-only': // Set defaults for HTTP-only mode result.http = { enabled: true, ...config.http }; break; case 'https-passthrough': // Set defaults for HTTPS passthrough result.https = { forwardSni: true, ...config.https }; // SNI forwarding doesn't do HTTP result.http = { enabled: false, ...config.http }; break; case 'https-terminate-to-http': // Set defaults for HTTPS termination to HTTP result.https = { ...config.https }; // Support HTTP access by default in this mode result.http = { enabled: true, redirectToHttps: true, ...config.http }; // Enable ACME by default result.acme = { enabled: true, maintenance: true, ...config.acme }; break; case 'https-terminate-to-https': // Similar to terminate-to-http but with different target handling result.https = { ...config.https }; result.http = { enabled: true, redirectToHttps: true, ...config.http }; result.acme = { enabled: true, maintenance: true, ...config.acme }; break; } return result; } /** * Validate a forwarding configuration * @param config The configuration to validate * @throws Error if the configuration is invalid */ public static validateConfig(config: ForwardConfig): void { // Validate common properties if (!config.target) { throw new Error('Forwarding configuration must include a target'); } if (!config.target.host || (Array.isArray(config.target.host) && config.target.host.length === 0)) { throw new Error('Target must include a host or array of hosts'); } if (!config.target.port || config.target.port <= 0 || config.target.port > 65535) { throw new Error('Target must include a valid port (1-65535)'); } // Type-specific validation switch (config.type) { case 'http-only': // HTTP-only needs http.enabled to be true if (config.http?.enabled === false) { throw new Error('HTTP-only forwarding must have HTTP enabled'); } break; case 'https-passthrough': // HTTPS passthrough doesn't support HTTP if (config.http?.enabled === true) { throw new Error('HTTPS passthrough does not support HTTP'); } // HTTPS passthrough doesn't work with ACME if (config.acme?.enabled === true) { throw new Error('HTTPS passthrough does not support ACME'); } break; case 'https-terminate-to-http': case 'https-terminate-to-https': // These modes support all options, nothing specific to validate break; } } }