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:
131
test/test.detection.ts
Normal file
131
test/test.detection.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as smartproxy from '../ts/index.js';
|
||||
|
||||
tap.test('Protocol Detection - TLS Detection', async () => {
|
||||
// Test TLS handshake detection
|
||||
const tlsHandshake = Buffer.from([
|
||||
0x16, // Handshake record type
|
||||
0x03, 0x01, // TLS 1.0
|
||||
0x00, 0x05, // Length: 5 bytes
|
||||
0x01, // ClientHello
|
||||
0x00, 0x00, 0x01, 0x00 // Handshake length and data
|
||||
]);
|
||||
|
||||
const detector = new smartproxy.detection.TlsDetector();
|
||||
expect(detector.canHandle(tlsHandshake)).toEqual(true);
|
||||
|
||||
const result = detector.detect(tlsHandshake);
|
||||
expect(result).toBeDefined();
|
||||
expect(result?.protocol).toEqual('tls');
|
||||
expect(result?.connectionInfo.tlsVersion).toEqual('TLSv1.0');
|
||||
});
|
||||
|
||||
tap.test('Protocol Detection - HTTP Detection', async () => {
|
||||
// Test HTTP request detection
|
||||
const httpRequest = Buffer.from(
|
||||
'GET /test HTTP/1.1\r\n' +
|
||||
'Host: example.com\r\n' +
|
||||
'User-Agent: TestClient/1.0\r\n' +
|
||||
'\r\n'
|
||||
);
|
||||
|
||||
const detector = new smartproxy.detection.HttpDetector();
|
||||
expect(detector.canHandle(httpRequest)).toEqual(true);
|
||||
|
||||
const result = detector.detect(httpRequest);
|
||||
expect(result).toBeDefined();
|
||||
expect(result?.protocol).toEqual('http');
|
||||
expect(result?.connectionInfo.method).toEqual('GET');
|
||||
expect(result?.connectionInfo.path).toEqual('/test');
|
||||
expect(result?.connectionInfo.domain).toEqual('example.com');
|
||||
});
|
||||
|
||||
tap.test('Protocol Detection - Main Detector TLS', async () => {
|
||||
const tlsHandshake = Buffer.from([
|
||||
0x16, // Handshake record type
|
||||
0x03, 0x03, // TLS 1.2
|
||||
0x00, 0x05, // Length: 5 bytes
|
||||
0x01, // ClientHello
|
||||
0x00, 0x00, 0x01, 0x00 // Handshake length and data
|
||||
]);
|
||||
|
||||
const result = await smartproxy.detection.ProtocolDetector.detect(tlsHandshake);
|
||||
expect(result.protocol).toEqual('tls');
|
||||
expect(result.connectionInfo.tlsVersion).toEqual('TLSv1.2');
|
||||
});
|
||||
|
||||
tap.test('Protocol Detection - Main Detector HTTP', async () => {
|
||||
const httpRequest = Buffer.from(
|
||||
'POST /api/test HTTP/1.1\r\n' +
|
||||
'Host: api.example.com\r\n' +
|
||||
'Content-Type: application/json\r\n' +
|
||||
'Content-Length: 2\r\n' +
|
||||
'\r\n' +
|
||||
'{}'
|
||||
);
|
||||
|
||||
const result = await smartproxy.detection.ProtocolDetector.detect(httpRequest);
|
||||
expect(result.protocol).toEqual('http');
|
||||
expect(result.connectionInfo.method).toEqual('POST');
|
||||
expect(result.connectionInfo.path).toEqual('/api/test');
|
||||
expect(result.connectionInfo.domain).toEqual('api.example.com');
|
||||
});
|
||||
|
||||
tap.test('Protocol Detection - Unknown Protocol', async () => {
|
||||
const unknownData = Buffer.from('UNKNOWN PROTOCOL DATA\r\n');
|
||||
|
||||
const result = await smartproxy.detection.ProtocolDetector.detect(unknownData);
|
||||
expect(result.protocol).toEqual('unknown');
|
||||
expect(result.isComplete).toEqual(true);
|
||||
});
|
||||
|
||||
tap.test('Protocol Detection - Fragmented HTTP', async () => {
|
||||
const connectionId = 'test-connection-1';
|
||||
|
||||
// First fragment
|
||||
const fragment1 = Buffer.from('GET /test HT');
|
||||
let result = await smartproxy.detection.ProtocolDetector.detectWithConnectionTracking(
|
||||
fragment1,
|
||||
connectionId
|
||||
);
|
||||
expect(result.protocol).toEqual('http');
|
||||
expect(result.isComplete).toEqual(false);
|
||||
|
||||
// Second fragment
|
||||
const fragment2 = Buffer.from('TP/1.1\r\nHost: example.com\r\n\r\n');
|
||||
result = await smartproxy.detection.ProtocolDetector.detectWithConnectionTracking(
|
||||
fragment2,
|
||||
connectionId
|
||||
);
|
||||
expect(result.protocol).toEqual('http');
|
||||
expect(result.isComplete).toEqual(true);
|
||||
expect(result.connectionInfo.method).toEqual('GET');
|
||||
expect(result.connectionInfo.path).toEqual('/test');
|
||||
expect(result.connectionInfo.domain).toEqual('example.com');
|
||||
});
|
||||
|
||||
tap.test('Protocol Detection - HTTP Methods', async () => {
|
||||
const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
||||
|
||||
for (const method of methods) {
|
||||
const request = Buffer.from(
|
||||
`${method} /test HTTP/1.1\r\n` +
|
||||
'Host: example.com\r\n' +
|
||||
'\r\n'
|
||||
);
|
||||
|
||||
const detector = new smartproxy.detection.HttpDetector();
|
||||
const result = detector.detect(request);
|
||||
expect(result?.connectionInfo.method).toEqual(method);
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('Protocol Detection - Invalid Data', async () => {
|
||||
// Binary data that's not a valid protocol
|
||||
const binaryData = Buffer.from([0xFF, 0xFE, 0xFD, 0xFC, 0xFB]);
|
||||
|
||||
const result = await smartproxy.detection.ProtocolDetector.detect(binaryData);
|
||||
expect(result.protocol).toEqual('unknown');
|
||||
});
|
||||
|
||||
tap.start();
|
Reference in New Issue
Block a user