feat(smart-proxy): add typed Rust config serialization and regex header contract coverage
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { IMetrics, IBackendMetrics, IProtocolCacheEntry, IProtocolDistribution, IThroughputData, IThroughputHistoryPoint } from './models/metrics-types.js';
|
||||
import type { RustProxyBridge } from './rust-proxy-bridge.js';
|
||||
import type { IRustBackendMetrics, IRustIpMetrics, IRustMetricsSnapshot, IRustRouteMetrics } from './models/rust-types.js';
|
||||
|
||||
/**
|
||||
* Adapts Rust JSON metrics to the IMetrics interface.
|
||||
@@ -14,7 +15,7 @@ import type { RustProxyBridge } from './rust-proxy-bridge.js';
|
||||
*/
|
||||
export class RustMetricsAdapter implements IMetrics {
|
||||
private bridge: RustProxyBridge;
|
||||
private cache: any = null;
|
||||
private cache: IRustMetricsSnapshot | null = null;
|
||||
private pollTimer: ReturnType<typeof setInterval> | null = null;
|
||||
private pollIntervalMs: number;
|
||||
|
||||
@@ -65,8 +66,8 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
byRoute: (): Map<string, number> => {
|
||||
const result = new Map<string, number>();
|
||||
if (this.cache?.routes) {
|
||||
for (const [name, rm] of Object.entries(this.cache.routes)) {
|
||||
result.set(name, (rm as any).activeConnections ?? 0);
|
||||
for (const [name, rm] of Object.entries(this.cache.routes) as Array<[string, IRustRouteMetrics]>) {
|
||||
result.set(name, rm.activeConnections ?? 0);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -74,8 +75,8 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
byIP: (): Map<string, number> => {
|
||||
const result = new Map<string, number>();
|
||||
if (this.cache?.ips) {
|
||||
for (const [ip, im] of Object.entries(this.cache.ips)) {
|
||||
result.set(ip, (im as any).activeConnections ?? 0);
|
||||
for (const [ip, im] of Object.entries(this.cache.ips) as Array<[string, IRustIpMetrics]>) {
|
||||
result.set(ip, im.activeConnections ?? 0);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -83,8 +84,8 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
topIPs: (limit: number = 10): Array<{ ip: string; count: number }> => {
|
||||
const result: Array<{ ip: string; count: number }> = [];
|
||||
if (this.cache?.ips) {
|
||||
for (const [ip, im] of Object.entries(this.cache.ips)) {
|
||||
result.push({ ip, count: (im as any).activeConnections ?? 0 });
|
||||
for (const [ip, im] of Object.entries(this.cache.ips) as Array<[string, IRustIpMetrics]>) {
|
||||
result.push({ ip, count: im.activeConnections ?? 0 });
|
||||
}
|
||||
}
|
||||
result.sort((a, b) => b.count - a.count);
|
||||
@@ -93,8 +94,8 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
domainRequestsByIP: (): Map<string, Map<string, number>> => {
|
||||
const result = new Map<string, Map<string, number>>();
|
||||
if (this.cache?.ips) {
|
||||
for (const [ip, im] of Object.entries(this.cache.ips)) {
|
||||
const dr = (im as any).domainRequests;
|
||||
for (const [ip, im] of Object.entries(this.cache.ips) as Array<[string, IRustIpMetrics]>) {
|
||||
const dr = im.domainRequests;
|
||||
if (dr && typeof dr === 'object') {
|
||||
const domainMap = new Map<string, number>();
|
||||
for (const [domain, count] of Object.entries(dr)) {
|
||||
@@ -111,8 +112,8 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
topDomainRequests: (limit: number = 20): Array<{ ip: string; domain: string; count: number }> => {
|
||||
const result: Array<{ ip: string; domain: string; count: number }> = [];
|
||||
if (this.cache?.ips) {
|
||||
for (const [ip, im] of Object.entries(this.cache.ips)) {
|
||||
const dr = (im as any).domainRequests;
|
||||
for (const [ip, im] of Object.entries(this.cache.ips) as Array<[string, IRustIpMetrics]>) {
|
||||
const dr = im.domainRequests;
|
||||
if (dr && typeof dr === 'object') {
|
||||
for (const [domain, count] of Object.entries(dr)) {
|
||||
result.push({ ip, domain, count: count as number });
|
||||
@@ -176,7 +177,7 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
},
|
||||
history: (seconds: number): Array<IThroughputHistoryPoint> => {
|
||||
if (!this.cache?.throughputHistory) return [];
|
||||
return this.cache.throughputHistory.slice(-seconds).map((p: any) => ({
|
||||
return this.cache.throughputHistory.slice(-seconds).map((p) => ({
|
||||
timestamp: p.timestampMs,
|
||||
in: p.bytesIn,
|
||||
out: p.bytesOut,
|
||||
@@ -185,10 +186,10 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
byRoute: (_windowSeconds?: number): Map<string, IThroughputData> => {
|
||||
const result = new Map<string, IThroughputData>();
|
||||
if (this.cache?.routes) {
|
||||
for (const [name, rm] of Object.entries(this.cache.routes)) {
|
||||
for (const [name, rm] of Object.entries(this.cache.routes) as Array<[string, IRustRouteMetrics]>) {
|
||||
result.set(name, {
|
||||
in: (rm as any).throughputInBytesPerSec ?? 0,
|
||||
out: (rm as any).throughputOutBytesPerSec ?? 0,
|
||||
in: rm.throughputInBytesPerSec ?? 0,
|
||||
out: rm.throughputOutBytesPerSec ?? 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -197,10 +198,10 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
byIP: (_windowSeconds?: number): Map<string, IThroughputData> => {
|
||||
const result = new Map<string, IThroughputData>();
|
||||
if (this.cache?.ips) {
|
||||
for (const [ip, im] of Object.entries(this.cache.ips)) {
|
||||
for (const [ip, im] of Object.entries(this.cache.ips) as Array<[string, IRustIpMetrics]>) {
|
||||
result.set(ip, {
|
||||
in: (im as any).throughputInBytesPerSec ?? 0,
|
||||
out: (im as any).throughputOutBytesPerSec ?? 0,
|
||||
in: im.throughputInBytesPerSec ?? 0,
|
||||
out: im.throughputOutBytesPerSec ?? 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -236,23 +237,22 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
byBackend: (): Map<string, IBackendMetrics> => {
|
||||
const result = new Map<string, IBackendMetrics>();
|
||||
if (this.cache?.backends) {
|
||||
for (const [key, bm] of Object.entries(this.cache.backends)) {
|
||||
const m = bm as any;
|
||||
const totalTimeUs = m.totalConnectTimeUs ?? 0;
|
||||
const count = m.connectCount ?? 0;
|
||||
const poolHits = m.poolHits ?? 0;
|
||||
const poolMisses = m.poolMisses ?? 0;
|
||||
for (const [key, bm] of Object.entries(this.cache.backends) as Array<[string, IRustBackendMetrics]>) {
|
||||
const totalTimeUs = bm.totalConnectTimeUs ?? 0;
|
||||
const count = bm.connectCount ?? 0;
|
||||
const poolHits = bm.poolHits ?? 0;
|
||||
const poolMisses = bm.poolMisses ?? 0;
|
||||
const poolTotal = poolHits + poolMisses;
|
||||
result.set(key, {
|
||||
protocol: m.protocol ?? 'unknown',
|
||||
activeConnections: m.activeConnections ?? 0,
|
||||
totalConnections: m.totalConnections ?? 0,
|
||||
connectErrors: m.connectErrors ?? 0,
|
||||
handshakeErrors: m.handshakeErrors ?? 0,
|
||||
requestErrors: m.requestErrors ?? 0,
|
||||
protocol: bm.protocol ?? 'unknown',
|
||||
activeConnections: bm.activeConnections ?? 0,
|
||||
totalConnections: bm.totalConnections ?? 0,
|
||||
connectErrors: bm.connectErrors ?? 0,
|
||||
handshakeErrors: bm.handshakeErrors ?? 0,
|
||||
requestErrors: bm.requestErrors ?? 0,
|
||||
avgConnectTimeMs: count > 0 ? (totalTimeUs / count) / 1000 : 0,
|
||||
poolHitRate: poolTotal > 0 ? poolHits / poolTotal : 0,
|
||||
h2Failures: m.h2Failures ?? 0,
|
||||
h2Failures: bm.h2Failures ?? 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -261,8 +261,8 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
protocols: (): Map<string, string> => {
|
||||
const result = new Map<string, string>();
|
||||
if (this.cache?.backends) {
|
||||
for (const [key, bm] of Object.entries(this.cache.backends)) {
|
||||
result.set(key, (bm as any).protocol ?? 'unknown');
|
||||
for (const [key, bm] of Object.entries(this.cache.backends) as Array<[string, IRustBackendMetrics]>) {
|
||||
result.set(key, bm.protocol ?? 'unknown');
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -270,9 +270,8 @@ export class RustMetricsAdapter implements IMetrics {
|
||||
topByErrors: (limit: number = 10): Array<{ backend: string; errors: number }> => {
|
||||
const result: Array<{ backend: string; errors: number }> = [];
|
||||
if (this.cache?.backends) {
|
||||
for (const [key, bm] of Object.entries(this.cache.backends)) {
|
||||
const m = bm as any;
|
||||
const errors = (m.connectErrors ?? 0) + (m.handshakeErrors ?? 0) + (m.requestErrors ?? 0);
|
||||
for (const [key, bm] of Object.entries(this.cache.backends) as Array<[string, IRustBackendMetrics]>) {
|
||||
const errors = (bm.connectErrors ?? 0) + (bm.handshakeErrors ?? 0) + (bm.requestErrors ?? 0);
|
||||
if (errors > 0) result.push({ backend: key, errors });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user