@casoon/auditmysite
Version:
Professional website analysis suite with robust accessibility testing, Core Web Vitals performance monitoring, SEO analysis, and content optimization insights. Features isolated browser contexts, retry mechanisms, and comprehensive API endpoints for profe
268 lines • 9.62 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetworkThrottler = void 0;
const events_1 = require("events");
class NetworkThrottler extends events_1.EventEmitter {
constructor(config = {}) {
super();
this.requestQueue = [];
this.activeRequests = new Map();
this.requestHistory = [];
this.isThrottling = false;
this.lastRequestTime = 0;
this.burstCount = 0;
this.burstStartTime = Date.now();
this.backoffDelay = 0;
this.config = {
requestsPerSecond: 10,
requestsPerMinute: 600,
concurrentRequests: 5,
delayBetweenRequests: 100,
burstLimit: 20,
burstWindow: 1000,
enableBackoff: true,
backoffMultiplier: 2,
maxBackoffDelay: 5000,
...config
};
this.stats = {
totalRequests: 0,
successfulRequests: 0,
throttledRequests: 0,
failedRequests: 0,
averageResponseTime: 0,
currentConcurrentRequests: 0,
requestsInLastSecond: 0,
requestsInLastMinute: 0,
isThrottling: false
};
// Cleanup alte Request-Historie
setInterval(() => this.cleanupHistory(), 60000); // Jede Minute
}
async throttleRequest(requestFn, url, method = 'GET', priority = 5, maxRetries = 3) {
const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const request = {
id: requestId,
url,
method,
timestamp: new Date(),
priority,
retryCount: 0,
maxRetries
};
// Request zur Queue hinzufügen
this.addToQueue(request);
// Warten bis Request ausgeführt werden kann
await this.waitForSlot(request);
try {
// Request ausführen
const startTime = Date.now();
const result = await this.executeRequest(request, requestFn);
const responseTime = Date.now() - startTime;
// Statistiken aktualisieren
this.updateStats(true, responseTime);
this.recordRequestSuccess(responseTime);
this.emit('request:success', { request, responseTime, timestamp: new Date() });
return result;
}
catch (error) {
// Retry-Logic
if (request.retryCount < request.maxRetries) {
request.retryCount++;
console.warn(`🔄 Retrying request ${request.id} (attempt ${request.retryCount}/${request.maxRetries})`);
// Backoff-Delay
if (this.config.enableBackoff) {
this.backoffDelay = Math.min(this.backoffDelay * this.config.backoffMultiplier, this.config.maxBackoffDelay);
await this.delay(this.backoffDelay);
}
// Request erneut zur Queue hinzufügen
this.addToQueue(request);
return this.throttleRequest(requestFn, url, method, priority, maxRetries);
}
// Max Retries erreicht
this.updateStats(false, 0);
this.recordRequestFailure();
this.emit('request:failed', { request, error: String(error), timestamp: new Date() });
throw error;
}
finally {
// Request aus aktiven Requests entfernen
this.activeRequests.delete(requestId);
this.stats.currentConcurrentRequests = this.activeRequests.size;
}
}
addToQueue(request) {
this.requestQueue.push(request);
// Nach Priorität sortieren (höchste zuerst)
this.requestQueue.sort((a, b) => b.priority - a.priority);
this.emit('request:queued', { request, timestamp: new Date() });
}
async waitForSlot(request) {
return new Promise((resolve) => {
const checkSlot = () => {
if (this.canExecuteRequest()) {
resolve();
}
else {
// Kurz warten und erneut prüfen
setTimeout(checkSlot, 50);
}
};
checkSlot();
});
}
canExecuteRequest() {
// Concurrent Requests Limit
if (this.activeRequests.size >= this.config.concurrentRequests) {
return false;
}
// Rate Limiting
const now = Date.now();
// Requests pro Sekunde
const requestsInLastSecond = this.requestHistory.filter(req => now - req.timestamp.getTime() < 1000).length;
if (requestsInLastSecond >= this.config.requestsPerSecond) {
return false;
}
// Requests pro Minute
const requestsInLastMinute = this.requestHistory.filter(req => now - req.timestamp.getTime() < 60000).length;
if (requestsInLastMinute >= this.config.requestsPerMinute) {
return false;
}
// Burst Limiting
if (this.burstCount >= this.config.burstLimit) {
const burstAge = now - this.burstStartTime;
if (burstAge < this.config.burstWindow) {
return false;
}
else {
// Burst Window reset
this.burstCount = 0;
this.burstStartTime = now;
}
}
// Delay zwischen Requests
if (now - this.lastRequestTime < this.config.delayBetweenRequests) {
return false;
}
return true;
}
async executeRequest(request, requestFn) {
// Request als aktiv markieren
this.activeRequests.set(request.id, request);
this.stats.currentConcurrentRequests = this.activeRequests.size;
// Burst Counter erhöhen
this.burstCount++;
this.lastRequestTime = Date.now();
// Request ausführen
const result = await requestFn();
// Backoff zurücksetzen bei Erfolg
if (this.config.enableBackoff) {
this.backoffDelay = 0;
}
return result;
}
updateStats(success, responseTime) {
this.stats.totalRequests++;
if (success) {
this.stats.successfulRequests++;
}
else {
this.stats.failedRequests++;
}
// Durchschnittliche Response-Zeit aktualisieren
if (responseTime > 0) {
const totalResponseTime = this.stats.averageResponseTime * (this.stats.successfulRequests - 1) + responseTime;
this.stats.averageResponseTime = totalResponseTime / this.stats.successfulRequests;
}
// Aktuelle Request-Raten aktualisieren
this.updateCurrentRates();
}
updateCurrentRates() {
const now = Date.now();
this.stats.requestsInLastSecond = this.requestHistory.filter(req => now - req.timestamp.getTime() < 1000).length;
this.stats.requestsInLastMinute = this.requestHistory.filter(req => now - req.timestamp.getTime() < 60000).length;
}
recordRequestSuccess(responseTime) {
this.requestHistory.push({
timestamp: new Date(),
success: true,
responseTime
});
}
recordRequestFailure() {
this.requestHistory.push({
timestamp: new Date(),
success: false,
responseTime: 0
});
}
cleanupHistory() {
const cutoffTime = Date.now() - 60000; // Nur letzte Minute behalten
this.requestHistory = this.requestHistory.filter(req => req.timestamp.getTime() > cutoffTime);
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Public Methods
getStats() {
return { ...this.stats };
}
getQueueSize() {
return this.requestQueue.length;
}
getActiveRequests() {
return this.activeRequests.size;
}
getThrottlingStatus() {
return this.isThrottling;
}
// Konfiguration ändern
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
this.emit('config:updated', { config: this.config, timestamp: new Date() });
}
// Queue Management
clearQueue() {
this.requestQueue = [];
this.emit('queue:cleared', { timestamp: new Date() });
}
pauseThrottling() {
this.isThrottling = true;
this.stats.isThrottling = true;
this.emit('throttling:paused', { timestamp: new Date() });
}
resumeThrottling() {
this.isThrottling = false;
this.stats.isThrottling = false;
this.emit('throttling:resumed', { timestamp: new Date() });
}
// Utility Methods
getConfig() {
return { ...this.config };
}
resetStats() {
this.stats = {
totalRequests: 0,
successfulRequests: 0,
throttledRequests: 0,
failedRequests: 0,
averageResponseTime: 0,
currentConcurrentRequests: 0,
requestsInLastSecond: 0,
requestsInLastMinute: 0,
isThrottling: false
};
this.requestHistory = [];
this.emit('stats:reset', { timestamp: new Date() });
}
// Cleanup
async cleanup() {
this.clearQueue();
this.activeRequests.clear();
this.requestHistory = [];
this.resetStats();
this.emit('throttler:cleanup', { timestamp: new Date() });
}
}
exports.NetworkThrottler = NetworkThrottler;
//# sourceMappingURL=network-throttler.js.map