fix(strcuture): refactor responsibilities

This commit is contained in:
2025-05-19 17:28:05 +00:00
parent 8fb67922a5
commit 465148d553
62 changed files with 1414 additions and 2066 deletions

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js';
import { NetworkProxy } from '../network-proxy/index.js';
import { HttpProxy } from '../http-proxy/index.js';
import type { IRouteConfig, IRouteTls } from './models/route-types.js';
import type { IAcmeOptions } from './models/interfaces.js';
import { CertStore } from './cert-store.js';
@ -25,7 +25,7 @@ export interface ICertificateData {
export class SmartCertManager {
private certStore: CertStore;
private smartAcme: plugins.smartacme.SmartAcme | null = null;
private networkProxy: NetworkProxy | null = null;
private httpProxy: HttpProxy | null = null;
private renewalTimer: NodeJS.Timeout | null = null;
private pendingChallenges: Map<string, string> = new Map();
private challengeRoute: IRouteConfig | null = null;
@ -68,8 +68,8 @@ export class SmartCertManager {
}
}
public setNetworkProxy(networkProxy: NetworkProxy): void {
this.networkProxy = networkProxy;
public setHttpProxy(httpProxy: HttpProxy): void {
this.httpProxy = httpProxy;
}
@ -336,23 +336,23 @@ export class SmartCertManager {
}
/**
* Apply certificate to NetworkProxy
* Apply certificate to HttpProxy
*/
private async applyCertificate(domain: string, certData: ICertificateData): Promise<void> {
if (!this.networkProxy) {
console.warn('NetworkProxy not set, cannot apply certificate');
if (!this.httpProxy) {
console.warn('HttpProxy not set, cannot apply certificate');
return;
}
// Apply certificate to NetworkProxy
this.networkProxy.updateCertificate(domain, certData.cert, certData.key);
// Apply certificate to HttpProxy
this.httpProxy.updateCertificate(domain, certData.cert, certData.key);
// Also apply for wildcard if it's a subdomain
if (domain.includes('.') && !domain.startsWith('*.')) {
const parts = domain.split('.');
if (parts.length >= 2) {
const wildcardDomain = `*.${parts.slice(-2).join('.')}`;
this.networkProxy.updateCertificate(wildcardDomain, certData.cert, certData.key);
this.httpProxy.updateCertificate(wildcardDomain, certData.cert, certData.key);
}
}
}

View File

@ -1,47 +1,47 @@
import * as plugins from '../../plugins.js';
import { NetworkProxy } from '../network-proxy/index.js';
import { HttpProxy } from '../http-proxy/index.js';
import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
import type { IRouteConfig } from './models/route-types.js';
export class NetworkProxyBridge {
private networkProxy: NetworkProxy | null = null;
export class HttpProxyBridge {
private httpProxy: HttpProxy | null = null;
constructor(private settings: ISmartProxyOptions) {}
/**
* Get the NetworkProxy instance
* Get the HttpProxy instance
*/
public getNetworkProxy(): NetworkProxy | null {
return this.networkProxy;
public getHttpProxy(): HttpProxy | null {
return this.httpProxy;
}
/**
* Initialize NetworkProxy instance
* Initialize HttpProxy instance
*/
public async initialize(): Promise<void> {
if (!this.networkProxy && this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0) {
const networkProxyOptions: any = {
port: this.settings.networkProxyPort!,
if (!this.httpProxy && this.settings.useHttpProxy && this.settings.useHttpProxy.length > 0) {
const httpProxyOptions: any = {
port: this.settings.httpProxyPort!,
portProxyIntegration: true,
logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info'
};
this.networkProxy = new NetworkProxy(networkProxyOptions);
console.log(`Initialized NetworkProxy on port ${this.settings.networkProxyPort}`);
this.httpProxy = new HttpProxy(httpProxyOptions);
console.log(`Initialized HttpProxy on port ${this.settings.httpProxyPort}`);
// Apply route configurations to NetworkProxy
await this.syncRoutesToNetworkProxy(this.settings.routes || []);
// Apply route configurations to HttpProxy
await this.syncRoutesToHttpProxy(this.settings.routes || []);
}
}
/**
* Sync routes to NetworkProxy
* Sync routes to HttpProxy
*/
public async syncRoutesToNetworkProxy(routes: IRouteConfig[]): Promise<void> {
if (!this.networkProxy) return;
public async syncRoutesToHttpProxy(routes: IRouteConfig[]): Promise<void> {
if (!this.httpProxy) return;
// Convert routes to NetworkProxy format
const networkProxyConfigs = routes
// Convert routes to HttpProxy format
const httpProxyConfigs = routes
.filter(route => {
// Check if this route matches any of the specified network proxy ports
const routePorts = Array.isArray(route.match.ports)
@ -49,20 +49,20 @@ export class NetworkProxyBridge {
: [route.match.ports];
return routePorts.some(port =>
this.settings.useNetworkProxy?.includes(port)
this.settings.useHttpProxy?.includes(port)
);
})
.map(route => this.routeToNetworkProxyConfig(route));
.map(route => this.routeToHttpProxyConfig(route));
// Apply configurations to NetworkProxy
await this.networkProxy.updateRouteConfigs(networkProxyConfigs);
// Apply configurations to HttpProxy
await this.httpProxy.updateRouteConfigs(httpProxyConfigs);
}
/**
* Convert route to NetworkProxy configuration
* Convert route to HttpProxy configuration
*/
private routeToNetworkProxyConfig(route: IRouteConfig): any {
// Convert route to NetworkProxy domain config format
private routeToHttpProxyConfig(route: IRouteConfig): any {
// Convert route to HttpProxy domain config format
return {
domain: route.match.domains?.[0] || '*',
target: route.action.target,
@ -72,36 +72,36 @@ export class NetworkProxyBridge {
}
/**
* Check if connection should use NetworkProxy
* Check if connection should use HttpProxy
*/
public shouldUseNetworkProxy(connection: IConnectionRecord, routeMatch: any): boolean {
// Only use NetworkProxy for TLS termination
public shouldUseHttpProxy(connection: IConnectionRecord, routeMatch: any): boolean {
// Only use HttpProxy for TLS termination
return (
routeMatch.route.action.tls?.mode === 'terminate' ||
routeMatch.route.action.tls?.mode === 'terminate-and-reencrypt'
) && this.networkProxy !== null;
) && this.httpProxy !== null;
}
/**
* Forward connection to NetworkProxy
* Forward connection to HttpProxy
*/
public async forwardToNetworkProxy(
public async forwardToHttpProxy(
connectionId: string,
socket: plugins.net.Socket,
record: IConnectionRecord,
initialChunk: Buffer,
networkProxyPort: number,
httpProxyPort: number,
cleanupCallback: (reason: string) => void
): Promise<void> {
if (!this.networkProxy) {
throw new Error('NetworkProxy not initialized');
if (!this.httpProxy) {
throw new Error('HttpProxy not initialized');
}
const proxySocket = new plugins.net.Socket();
await new Promise<void>((resolve, reject) => {
proxySocket.connect(networkProxyPort, 'localhost', () => {
console.log(`[${connectionId}] Connected to NetworkProxy for termination`);
proxySocket.connect(httpProxyPort, 'localhost', () => {
console.log(`[${connectionId}] Connected to HttpProxy for termination`);
resolve();
});
@ -132,21 +132,21 @@ export class NetworkProxyBridge {
}
/**
* Start NetworkProxy
* Start HttpProxy
*/
public async start(): Promise<void> {
if (this.networkProxy) {
await this.networkProxy.start();
if (this.httpProxy) {
await this.httpProxy.start();
}
}
/**
* Stop NetworkProxy
* Stop HttpProxy
*/
public async stop(): Promise<void> {
if (this.networkProxy) {
await this.networkProxy.stop();
this.networkProxy = null;
if (this.httpProxy) {
await this.httpProxy.stop();
this.httpProxy = null;
}
}
}

View File

@ -14,12 +14,15 @@ export { ConnectionManager } from './connection-manager.js';
export { SecurityManager } from './security-manager.js';
export { TimeoutManager } from './timeout-manager.js';
export { TlsManager } from './tls-manager.js';
export { NetworkProxyBridge } from './network-proxy-bridge.js';
export { HttpProxyBridge } from './http-proxy-bridge.js';
// Export route-based components
export { RouteManager } from './route-manager.js';
export { RouteConnectionHandler } from './route-connection-handler.js';
export { NFTablesManager } from './nftables-manager.js';
// Export certificate management
export { SmartCertManager } from './certificate-manager.js';
// Export all helper functions from the utils directory
export * from './utils/index.js';

View File

@ -94,9 +94,9 @@ export interface ISmartProxyOptions {
keepAliveInactivityMultiplier?: number; // Multiplier for inactivity timeout for keep-alive connections
extendedKeepAliveLifetime?: number; // Extended lifetime for keep-alive connections (ms)
// NetworkProxy integration
useNetworkProxy?: number[]; // Array of ports to forward to NetworkProxy
networkProxyPort?: number; // Port where NetworkProxy is listening (default: 8443)
// HttpProxy integration
useHttpProxy?: number[]; // Array of ports to forward to HttpProxy
httpProxyPort?: number; // Port where HttpProxy is listening (default: 8443)
/**
* Global ACME configuration options for SmartProxy

View File

@ -60,10 +60,10 @@ export class PortManager {
// Start listening on the port
return new Promise<void>((resolve, reject) => {
server.listen(port, () => {
const isNetworkProxyPort = this.settings.useNetworkProxy?.includes(port);
const isHttpProxyPort = this.settings.useHttpProxy?.includes(port);
console.log(
`SmartProxy -> OK: Now listening on port ${port}${
isNetworkProxyPort ? ' (NetworkProxy forwarding enabled)' : ''
isHttpProxyPort ? ' (HttpProxy forwarding enabled)' : ''
}`
);

View File

@ -5,10 +5,11 @@ import type { IRouteConfig, IRouteAction, IRouteContext } from './models/route-t
import { ConnectionManager } from './connection-manager.js';
import { SecurityManager } from './security-manager.js';
import { TlsManager } from './tls-manager.js';
import { NetworkProxyBridge } from './network-proxy-bridge.js';
import { HttpProxyBridge } from './http-proxy-bridge.js';
import { TimeoutManager } from './timeout-manager.js';
import { RouteManager } from './route-manager.js';
import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js';
import { RedirectHandler, StaticHandler } from '../http-proxy/handlers/index.js';
/**
* Handles new connection processing and setup logic with support for route-based configuration
@ -24,7 +25,7 @@ export class RouteConnectionHandler {
private connectionManager: ConnectionManager,
private securityManager: SecurityManager,
private tlsManager: TlsManager,
private networkProxyBridge: NetworkProxyBridge,
private httpProxyBridge: HttpProxyBridge,
private timeoutManager: TimeoutManager,
private routeManager: RouteManager
) {
@ -530,22 +531,22 @@ export class RouteConnectionHandler {
case 'terminate':
case 'terminate-and-reencrypt':
// For TLS termination, use NetworkProxy
if (this.networkProxyBridge.getNetworkProxy()) {
// For TLS termination, use HttpProxy
if (this.httpProxyBridge.getHttpProxy()) {
if (this.settings.enableDetailedLogging) {
console.log(
`[${connectionId}] Using NetworkProxy for TLS termination to ${action.target.host}`
`[${connectionId}] Using HttpProxy for TLS termination to ${action.target.host}`
);
}
// If we have an initial chunk with TLS data, start processing it
if (initialChunk && record.isTLS) {
this.networkProxyBridge.forwardToNetworkProxy(
this.httpProxyBridge.forwardToHttpProxy(
connectionId,
socket,
record,
initialChunk,
this.settings.networkProxyPort,
this.settings.httpProxyPort || 8443,
(reason) => this.connectionManager.initiateCleanupOnce(record, reason)
);
return;
@ -557,9 +558,9 @@ export class RouteConnectionHandler {
this.connectionManager.cleanupConnection(record, 'tls_error');
return;
} else {
console.log(`[${connectionId}] NetworkProxy not available for TLS termination`);
console.log(`[${connectionId}] HttpProxy not available for TLS termination`);
socket.end();
this.connectionManager.cleanupConnection(record, 'no_network_proxy');
this.connectionManager.cleanupConnection(record, 'no_http_proxy');
return;
}
}
@ -621,87 +622,20 @@ export class RouteConnectionHandler {
record: IConnectionRecord,
route: IRouteConfig
): void {
const connectionId = record.id;
const action = route.action;
// We should have a redirect configuration
if (!action.redirect) {
console.log(`[${connectionId}] Redirect action missing redirect configuration`);
socket.end();
this.connectionManager.cleanupConnection(record, 'missing_redirect');
return;
}
// For TLS connections, we can't do redirects at the TCP level
if (record.isTLS) {
console.log(`[${connectionId}] Cannot redirect TLS connection at TCP level`);
console.log(`[${record.id}] Cannot redirect TLS connection at TCP level`);
socket.end();
this.connectionManager.cleanupConnection(record, 'tls_redirect_error');
return;
}
// Wait for the first HTTP request to perform the redirect
const dataListeners: ((chunk: Buffer) => void)[] = [];
const httpDataHandler = (chunk: Buffer) => {
// Remove all data listeners to avoid duplicated processing
for (const listener of dataListeners) {
socket.removeListener('data', listener);
}
// Parse HTTP request to get path
try {
const headersEnd = chunk.indexOf('\r\n\r\n');
if (headersEnd === -1) {
// Not a complete HTTP request, need more data
socket.once('data', httpDataHandler);
dataListeners.push(httpDataHandler);
return;
}
const httpHeaders = chunk.slice(0, headersEnd).toString();
const requestLine = httpHeaders.split('\r\n')[0];
const [method, path] = requestLine.split(' ');
// Extract Host header
const hostMatch = httpHeaders.match(/Host: (.+?)(\r\n|\r|\n|$)/i);
const host = hostMatch ? hostMatch[1].trim() : record.lockedDomain || '';
// Process the redirect URL with template variables
let redirectUrl = action.redirect.to;
redirectUrl = redirectUrl.replace(/\{domain\}/g, host);
redirectUrl = redirectUrl.replace(/\{path\}/g, path || '');
redirectUrl = redirectUrl.replace(/\{port\}/g, record.localPort.toString());
// Prepare the HTTP redirect response
const redirectResponse = [
`HTTP/1.1 ${action.redirect.status} Moved`,
`Location: ${redirectUrl}`,
'Connection: close',
'Content-Length: 0',
'',
'',
].join('\r\n');
if (this.settings.enableDetailedLogging) {
console.log(
`[${connectionId}] Redirecting to ${redirectUrl} with status ${action.redirect.status}`
);
}
// Send the redirect response
socket.end(redirectResponse);
this.connectionManager.initiateCleanupOnce(record, 'redirect_complete');
} catch (err) {
console.log(`[${connectionId}] Error processing HTTP redirect: ${err}`);
socket.end();
this.connectionManager.initiateCleanupOnce(record, 'redirect_error');
}
};
// Setup the HTTP data handler
socket.once('data', httpDataHandler);
dataListeners.push(httpDataHandler);
// Delegate to HttpProxy's RedirectHandler
RedirectHandler.handleRedirect(socket, route, {
connectionId: record.id,
connectionManager: this.connectionManager,
settings: this.settings
});
}
/**
@ -733,221 +667,12 @@ export class RouteConnectionHandler {
record: IConnectionRecord,
route: IRouteConfig
): Promise<void> {
const connectionId = record.id;
if (!route.action.handler) {
console.error(`[${connectionId}] Static route '${route.name}' has no handler`);
socket.end();
this.connectionManager.cleanupConnection(record, 'no_handler');
return;
}
let buffer = Buffer.alloc(0);
let processingData = false;
const handleHttpData = async (chunk: Buffer) => {
// Accumulate the data
buffer = Buffer.concat([buffer, chunk]);
// Prevent concurrent processing of the same buffer
if (processingData) return;
processingData = true;
try {
// Process data until we have a complete request or need more data
await processBuffer();
} finally {
processingData = false;
}
};
const processBuffer = async () => {
// Look for end of HTTP headers
const headerEndIndex = buffer.indexOf('\r\n\r\n');
if (headerEndIndex === -1) {
// Need more data
if (buffer.length > 8192) {
// Prevent excessive buffering
console.error(`[${connectionId}] HTTP headers too large`);
socket.end();
this.connectionManager.cleanupConnection(record, 'headers_too_large');
}
return; // Wait for more data to arrive
}
// Parse the HTTP request
const headerBuffer = buffer.slice(0, headerEndIndex);
const headers = headerBuffer.toString();
const lines = headers.split('\r\n');
if (lines.length === 0) {
console.error(`[${connectionId}] Invalid HTTP request`);
socket.end();
this.connectionManager.cleanupConnection(record, 'invalid_request');
return;
}
// Parse request line
const requestLine = lines[0];
const requestParts = requestLine.split(' ');
if (requestParts.length < 3) {
console.error(`[${connectionId}] Invalid HTTP request line`);
socket.end();
this.connectionManager.cleanupConnection(record, 'invalid_request_line');
return;
}
const [method, path, httpVersion] = requestParts;
// Parse headers
const headersMap: Record<string, string> = {};
for (let i = 1; i < lines.length; i++) {
const colonIndex = lines[i].indexOf(':');
if (colonIndex > 0) {
const key = lines[i].slice(0, colonIndex).trim().toLowerCase();
const value = lines[i].slice(colonIndex + 1).trim();
headersMap[key] = value;
}
}
// Check for Content-Length to handle request body
const requestBodyLength = parseInt(headersMap['content-length'] || '0', 10);
const bodyStartIndex = headerEndIndex + 4; // Skip the \r\n\r\n
// If there's a body, ensure we have the full body
if (requestBodyLength > 0) {
const totalExpectedLength = bodyStartIndex + requestBodyLength;
// If we don't have the complete body yet, wait for more data
if (buffer.length < totalExpectedLength) {
// Implement a reasonable body size limit to prevent memory issues
if (requestBodyLength > 1024 * 1024) {
// 1MB limit
console.error(`[${connectionId}] Request body too large`);
socket.end();
this.connectionManager.cleanupConnection(record, 'body_too_large');
return;
}
return; // Wait for more data
}
}
// Extract query string if present
let pathname = path;
let query: string | undefined;
const queryIndex = path.indexOf('?');
if (queryIndex !== -1) {
pathname = path.slice(0, queryIndex);
query = path.slice(queryIndex + 1);
}
try {
// Get request body if present
let requestBody: Buffer | undefined;
if (requestBodyLength > 0) {
requestBody = buffer.slice(bodyStartIndex, bodyStartIndex + requestBodyLength);
}
// Pause socket to prevent data loss during async processing
socket.pause();
// Remove the data listener since we're handling the request
socket.removeListener('data', handleHttpData);
// Build route context with parsed HTTP information
const context: IRouteContext = {
port: record.localPort,
domain: record.lockedDomain || headersMap['host']?.split(':')[0],
clientIp: record.remoteIP,
serverIp: socket.localAddress!,
path: pathname,
query: query,
headers: headersMap,
method: method,
isTls: record.isTLS,
tlsVersion: record.tlsVersion,
routeName: route.name,
routeId: route.id,
timestamp: Date.now(),
connectionId,
};
// Since IRouteContext doesn't have a body property,
// we need an alternative approach to handle the body
let response;
if (requestBody) {
if (this.settings.enableDetailedLogging) {
console.log(
`[${connectionId}] Processing request with body (${requestBody.length} bytes)`
);
}
// Pass the body as an additional parameter by extending the context object
// This is not type-safe, but it allows handlers that expect a body to work
const extendedContext = {
...context,
// Provide both raw buffer and string representation
requestBody: requestBody,
requestBodyText: requestBody.toString(),
};
// Call the handler with the extended context
// The handler needs to know to look for the non-standard properties
response = await route.action.handler(extendedContext as any);
} else {
// Call the handler with the standard context
response = await route.action.handler(context);
}
// Prepare the HTTP response
const responseHeaders = response.headers || {};
const contentLength = Buffer.byteLength(response.body || '');
responseHeaders['Content-Length'] = contentLength.toString();
if (!responseHeaders['Content-Type']) {
responseHeaders['Content-Type'] = 'text/plain';
}
// Build the response
let httpResponse = `HTTP/1.1 ${response.status} ${getStatusText(response.status)}\r\n`;
for (const [key, value] of Object.entries(responseHeaders)) {
httpResponse += `${key}: ${value}\r\n`;
}
httpResponse += '\r\n';
// Send response
socket.write(httpResponse);
if (response.body) {
socket.write(response.body);
}
socket.end();
this.connectionManager.cleanupConnection(record, 'completed');
} catch (error) {
console.error(`[${connectionId}] Error in static handler: ${error}`);
// Send error response
const errorResponse =
'HTTP/1.1 500 Internal Server Error\r\n' +
'Content-Type: text/plain\r\n' +
'Content-Length: 21\r\n' +
'\r\n' +
'Internal Server Error';
socket.write(errorResponse);
socket.end();
this.connectionManager.cleanupConnection(record, 'handler_error');
}
};
// Listen for data
socket.on('data', handleHttpData);
// Ensure cleanup on socket close
socket.once('close', () => {
socket.removeListener('data', handleHttpData);
});
// Delegate to HttpProxy's StaticHandler
await StaticHandler.handleStatic(socket, route, {
connectionId: record.id,
connectionManager: this.connectionManager,
settings: this.settings
}, record);
}
/**
@ -1378,12 +1103,3 @@ export class RouteConnectionHandler {
}
}
// Helper function for status text
function getStatusText(status: number): string {
const statusTexts: Record<number, string> = {
200: 'OK',
404: 'Not Found',
500: 'Internal Server Error',
};
return statusTexts[status] || 'Unknown';
}

View File

@ -4,7 +4,7 @@ import * as plugins from '../../plugins.js';
import { ConnectionManager } from './connection-manager.js';
import { SecurityManager } from './security-manager.js';
import { TlsManager } from './tls-manager.js';
import { NetworkProxyBridge } from './network-proxy-bridge.js';
import { HttpProxyBridge } from './http-proxy-bridge.js';
import { TimeoutManager } from './timeout-manager.js';
import { PortManager } from './port-manager.js';
import { RouteManager } from './route-manager.js';
@ -49,7 +49,7 @@ export class SmartProxy extends plugins.EventEmitter {
private connectionManager: ConnectionManager;
private securityManager: SecurityManager;
private tlsManager: TlsManager;
private networkProxyBridge: NetworkProxyBridge;
private httpProxyBridge: HttpProxyBridge;
private timeoutManager: TimeoutManager;
public routeManager: RouteManager; // Made public for route management
private routeConnectionHandler: RouteConnectionHandler;
@ -123,7 +123,7 @@ export class SmartProxy extends plugins.EventEmitter {
keepAliveTreatment: settingsArg.keepAliveTreatment || 'extended',
keepAliveInactivityMultiplier: settingsArg.keepAliveInactivityMultiplier || 6,
extendedKeepAliveLifetime: settingsArg.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000,
networkProxyPort: settingsArg.networkProxyPort || 8443,
httpProxyPort: settingsArg.httpProxyPort || 8443,
};
// Normalize ACME options if provided (support both email and accountEmail)
@ -164,7 +164,7 @@ export class SmartProxy extends plugins.EventEmitter {
// Create other required components
this.tlsManager = new TlsManager(this.settings);
this.networkProxyBridge = new NetworkProxyBridge(this.settings);
this.httpProxyBridge = new HttpProxyBridge(this.settings);
// Initialize connection handler with route support
this.routeConnectionHandler = new RouteConnectionHandler(
@ -172,7 +172,7 @@ export class SmartProxy extends plugins.EventEmitter {
this.connectionManager,
this.securityManager,
this.tlsManager,
this.networkProxyBridge,
this.httpProxyBridge,
this.timeoutManager,
this.routeManager
);
@ -212,9 +212,9 @@ export class SmartProxy extends plugins.EventEmitter {
await this.updateRoutes(routes);
});
// Connect with NetworkProxy if available
if (this.networkProxyBridge.getNetworkProxy()) {
certManager.setNetworkProxy(this.networkProxyBridge.getNetworkProxy());
// Connect with HttpProxy if available
if (this.httpProxyBridge.getHttpProxy()) {
certManager.setHttpProxy(this.httpProxyBridge.getHttpProxy());
}
// Set the ACME state manager
@ -312,16 +312,16 @@ export class SmartProxy extends plugins.EventEmitter {
// Initialize certificate manager before starting servers
await this.initializeCertificateManager();
// Initialize and start NetworkProxy if needed
if (this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0) {
await this.networkProxyBridge.initialize();
// Initialize and start HttpProxy if needed
if (this.settings.useHttpProxy && this.settings.useHttpProxy.length > 0) {
await this.httpProxyBridge.initialize();
// Connect NetworkProxy with certificate manager
// Connect HttpProxy with certificate manager
if (this.certManager) {
this.certManager.setNetworkProxy(this.networkProxyBridge.getNetworkProxy());
this.certManager.setHttpProxy(this.httpProxyBridge.getHttpProxy());
}
await this.networkProxyBridge.start();
await this.httpProxyBridge.start();
}
// Validate the route configuration
@ -368,7 +368,7 @@ export class SmartProxy extends plugins.EventEmitter {
let completedTlsHandshakes = 0;
let pendingTlsHandshakes = 0;
let keepAliveConnections = 0;
let networkProxyConnections = 0;
let httpProxyConnections = 0;
// Get connection records for analysis
const connectionRecords = this.connectionManager.getConnections();
@ -392,7 +392,7 @@ export class SmartProxy extends plugins.EventEmitter {
}
if (record.usingNetworkProxy) {
networkProxyConnections++;
httpProxyConnections++;
}
maxIncoming = Math.max(maxIncoming, now - record.incomingStartTime);
@ -408,7 +408,7 @@ export class SmartProxy extends plugins.EventEmitter {
console.log(
`Active connections: ${connectionRecords.size}. ` +
`Types: TLS=${tlsConnections} (Completed=${completedTlsHandshakes}, Pending=${pendingTlsHandshakes}), ` +
`Non-TLS=${nonTlsConnections}, KeepAlive=${keepAliveConnections}, NetworkProxy=${networkProxyConnections}. ` +
`Non-TLS=${nonTlsConnections}, KeepAlive=${keepAliveConnections}, HttpProxy=${httpProxyConnections}. ` +
`Longest running: IN=${plugins.prettyMs(maxIncoming)}, OUT=${plugins.prettyMs(maxOutgoing)}. ` +
`Termination stats: ${JSON.stringify({
IN: terminationStats.incoming,
@ -460,8 +460,8 @@ export class SmartProxy extends plugins.EventEmitter {
// Clean up all active connections
this.connectionManager.clearConnections();
// Stop NetworkProxy
await this.networkProxyBridge.stop();
// Stop HttpProxy
await this.httpProxyBridge.stop();
// Clear ACME state manager
this.acmeStateManager.clear();
@ -574,9 +574,9 @@ export class SmartProxy extends plugins.EventEmitter {
// Update settings with the new routes
this.settings.routes = newRoutes;
// If NetworkProxy is initialized, resync the configurations
if (this.networkProxyBridge.getNetworkProxy()) {
await this.networkProxyBridge.syncRoutesToNetworkProxy(newRoutes);
// If HttpProxy is initialized, resync the configurations
if (this.httpProxyBridge.getHttpProxy()) {
await this.httpProxyBridge.syncRoutesToHttpProxy(newRoutes);
}
// Update certificate manager with new routes
@ -711,14 +711,14 @@ export class SmartProxy extends plugins.EventEmitter {
let tlsConnections = 0;
let nonTlsConnections = 0;
let keepAliveConnections = 0;
let networkProxyConnections = 0;
let httpProxyConnections = 0;
// Analyze active connections
for (const record of connectionRecords.values()) {
if (record.isTLS) tlsConnections++;
else nonTlsConnections++;
if (record.hasKeepAlive) keepAliveConnections++;
if (record.usingNetworkProxy) networkProxyConnections++;
if (record.usingNetworkProxy) httpProxyConnections++;
}
return {
@ -726,7 +726,7 @@ export class SmartProxy extends plugins.EventEmitter {
tlsConnections,
nonTlsConnections,
keepAliveConnections,
networkProxyConnections,
httpProxyConnections,
terminationStats,
acmeEnabled: !!this.certManager,
port80HandlerPort: this.certManager ? 80 : null,