feat(detection): add centralized protocol detection module

- Created ts/detection module for unified protocol detection
- Implemented TLS and HTTP detectors with fragmentation support
- Moved TLS detection logic from existing code to centralized module
- Updated RouteConnectionHandler to use ProtocolDetector for both TLS and HTTP
- Refactored ACME HTTP parsing to use detection module
- Added comprehensive tests for detection functionality
- Eliminated duplicate protocol detection code across codebase

This centralizes all non-destructive protocol detection into a single module,
improving code organization and reducing duplication between ACME and routing.
This commit is contained in:
Juergen Kunz
2025-07-21 19:40:01 +00:00
parent c84947068c
commit d47b048517
14 changed files with 1620 additions and 127 deletions

View File

@@ -0,0 +1,102 @@
/**
* Type definitions for protocol detection
*/
/**
* Supported protocol types that can be detected
*/
export type TProtocolType = 'tls' | 'http' | 'unknown';
/**
* HTTP method types
*/
export type THttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
/**
* TLS version identifiers
*/
export type TTlsVersion = 'SSLv3' | 'TLSv1.0' | 'TLSv1.1' | 'TLSv1.2' | 'TLSv1.3';
/**
* Connection information extracted from protocol detection
*/
export interface IConnectionInfo {
/**
* The detected protocol type
*/
protocol: TProtocolType;
/**
* Domain/hostname extracted from the connection
* - For TLS: from SNI extension
* - For HTTP: from Host header
*/
domain?: string;
/**
* HTTP-specific fields
*/
method?: THttpMethod;
path?: string;
httpVersion?: string;
headers?: Record<string, string>;
/**
* TLS-specific fields
*/
tlsVersion?: TTlsVersion;
sni?: string;
alpn?: string[];
cipherSuites?: number[];
}
/**
* Result of protocol detection
*/
export interface IDetectionResult {
/**
* The detected protocol type
*/
protocol: TProtocolType;
/**
* Extracted connection information
*/
connectionInfo: IConnectionInfo;
/**
* Any remaining buffer data after detection headers
* This can be used to continue processing the stream
*/
remainingBuffer?: Buffer;
/**
* Whether the detection is complete or needs more data
*/
isComplete: boolean;
/**
* Minimum bytes needed for complete detection (if incomplete)
*/
bytesNeeded?: number;
}
/**
* Options for protocol detection
*/
export interface IDetectionOptions {
/**
* Maximum bytes to buffer for detection (default: 8192)
*/
maxBufferSize?: number;
/**
* Timeout for detection in milliseconds (default: 5000)
*/
timeout?: number;
/**
* Whether to extract full headers or just essential info
*/
extractFullHeaders?: boolean;
}

View File

@@ -0,0 +1,115 @@
/**
* Interface definitions for protocol detection components
*/
import type { IDetectionResult, IDetectionOptions } from './detection-types.js';
/**
* Interface for protocol detectors
*/
export interface IProtocolDetector {
/**
* Detect protocol from buffer data
* @param buffer The buffer to analyze
* @param options Detection options
* @returns Detection result or null if protocol cannot be determined
*/
detect(buffer: Buffer, options?: IDetectionOptions): IDetectionResult | null;
/**
* Check if buffer potentially contains this protocol
* @param buffer The buffer to check
* @returns True if buffer might contain this protocol
*/
canHandle(buffer: Buffer): boolean;
/**
* Get the minimum bytes needed for detection
*/
getMinimumBytes(): number;
}
/**
* Interface for connection tracking during fragmented detection
*/
export interface IConnectionTracker {
/**
* Connection identifier
*/
id: string;
/**
* Accumulated buffer data
*/
buffer: Buffer;
/**
* Timestamp of first data
*/
startTime: number;
/**
* Current detection state
*/
state: 'detecting' | 'complete' | 'failed';
/**
* Partial detection result (if any)
*/
partialResult?: Partial<IDetectionResult>;
}
/**
* Interface for buffer accumulator (handles fragmented data)
*/
export interface IBufferAccumulator {
/**
* Add data to accumulator
*/
append(data: Buffer): void;
/**
* Get accumulated buffer
*/
getBuffer(): Buffer;
/**
* Get buffer length
*/
length(): number;
/**
* Clear accumulated data
*/
clear(): void;
/**
* Check if accumulator has enough data
*/
hasMinimumBytes(minBytes: number): boolean;
}
/**
* Detection events
*/
export interface IDetectionEvents {
/**
* Emitted when protocol is successfully detected
*/
detected: (result: IDetectionResult) => void;
/**
* Emitted when detection fails
*/
failed: (error: Error) => void;
/**
* Emitted when detection times out
*/
timeout: () => void;
/**
* Emitted when more data is needed
*/
needMoreData: (bytesNeeded: number) => void;
}