using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TechSec602.Advanced
{
///
/// Advanced 602TechSec client implementation with caching, logging, and performance optimizations
///
public class TechSec602AdvancedClient : TechSec602Client
{
private readonly ConcurrentDictionary _cache;
private readonly TechSec602Config _config;
private readonly ILogger _logger;
private readonly TechSec602CircuitBreaker _circuitBreaker;
///
/// Parameterless constructor - reads configuration from web.config
///
public TechSec602AdvancedClient() : this(TechSec602Config.FromAppSettings())
{
}
public TechSec602AdvancedClient(TechSec602Config config, ILogger logger = null) : base(config)
{
_config = config;
_cache = new ConcurrentDictionary();
_logger = logger;
_circuitBreaker = new TechSec602CircuitBreaker();
// Start cache cleanup timer
if (config.CacheResults)
{
StartCacheCleanup();
}
}
///
/// Enhanced verification with caching and performance monitoring
///
public new async Task VerifyRequestAsync(string url, string ipAddress = null, string apiKey = null)
{
var cacheKey = $"{url}|{ipAddress}|{apiKey}";
// Check cache first
if (_config.CacheResults && _cache.TryGetValue(cacheKey, out var cachedResult) && !cachedResult.IsExpired)
{
_logger?.LogDebug($"Cache hit for {url}");
return cachedResult.Result;
}
// Check circuit breaker
if (_circuitBreaker.IsOpen)
{
_logger?.LogInfo("Circuit breaker is open, failing open");
return new VerificationResult { IsAllowed = _config.FailOpen, Reason = "Service unavailable (circuit breaker open)", Success = false };
}
var startTime = DateTime.UtcNow;
// Call base verification
var result = await base.VerifyRequestAsync(url, ipAddress, apiKey);
var endTime = DateTime.UtcNow;
var duration = (endTime - startTime).TotalMilliseconds;
// Update circuit breaker
if (result.Success)
{
_circuitBreaker.RecordSuccess();
}
else
{
_circuitBreaker.RecordFailure();
}
// Log the request
if (_config.EnableLogging)
{
_logger?.LogInfo($"Verification: {url} | IP: {ipAddress} | Result: {result.IsAllowed} | Duration: {duration}ms | Reason: {result.Reason}");
}
// Cache successful results
if (_config.CacheResults && result.Success)
{
var expiry = DateTime.UtcNow.AddMinutes(_config.CacheTimeoutMinutes);
_cache.TryAdd(cacheKey, new CachedResult { Result = result, Expiry = expiry });
}
return result;
}
///
/// Batch verification for multiple URLs
///
public async Task> VerifyBatchAsync(List requests)
{
var tasks = requests.Select(req => VerifyRequestAsync(req.Url, req.IpAddress, req.ApiKey)).ToArray();
var results = await Task.WhenAll(tasks);
return results.ToList();
}
///
/// Pre-warm cache with common URLs
///
public async Task PreWarmCacheAsync(List urls)
{
var tasks = urls.Select(url => VerifyRequestAsync(url)).ToArray();
await Task.WhenAll(tasks);
_logger?.LogInfo($"Pre-warmed cache with {urls.Count} URLs");
}
///
/// Get cache statistics
///
public CacheStats GetCacheStats()
{
return new CacheStats
{
TotalEntries = _cache.Count,
ExpiredEntries = _cache.Values.Count(c => c.IsExpired)
};
}
///
/// Clear expired cache entries
///
public void ClearExpiredCache()
{
var expiredKeys = _cache.Where(kvp => kvp.Value.IsExpired).Select(kvp => kvp.Key).ToList();
foreach (var key in expiredKeys)
{
_cache.TryRemove(key, out _);
}
_logger?.LogDebug($"Cleared {expiredKeys.Count} expired cache entries");
}
private void StartCacheCleanup()
{
Task.Run(async () =>
{
while (true)
{
await Task.Delay(TimeSpan.FromMinutes(1));
ClearExpiredCache();
}
});
}
}
///
/// Cached verification result
///
public class CachedResult
{
public VerificationResult Result { get; set; }
public DateTime Expiry { get; set; }
public bool IsExpired => DateTime.UtcNow > Expiry;
}
///
/// Batch verification request
///
public class BatchVerificationRequest
{
public string Url { get; set; }
public string IpAddress { get; set; }
public string ApiKey { get; set; }
}
///
/// Cache statistics
///
public class CacheStats
{
public int TotalEntries { get; set; }
public int ExpiredEntries { get; set; }
}
///
/// Simple logging interface
///
public interface ILogger
{
void LogInfo(string message);
void LogDebug(string message);
void LogError(string message);
}
///
/// Basic console logger implementation
///
public class ConsoleLogger : ILogger
{
public void LogInfo(string message)
{
Console.WriteLine($"[INFO] {DateTime.Now}: {message}");
}
public void LogDebug(string message)
{
Console.WriteLine($"[DEBUG] {DateTime.Now}: {message}");
}
public void LogError(string message)
{
Console.WriteLine($"[ERROR] {DateTime.Now}: {message}");
}
}
///
/// Circuit breaker pattern for fault tolerance
///
public class TechSec602CircuitBreaker
{
private int _failureCount = 0;
private DateTime _lastFailureTime = DateTime.MinValue;
private readonly int _failureThreshold;
private readonly int _resetTimeoutMinutes;
public TechSec602CircuitBreaker(int failureThreshold = 5, int resetTimeoutMinutes = 5)
{
_failureThreshold = failureThreshold;
_resetTimeoutMinutes = resetTimeoutMinutes;
}
public bool IsOpen
{
get
{
if (_failureCount >= _failureThreshold)
{
return DateTime.UtcNow.Subtract(_lastFailureTime).TotalMinutes < _resetTimeoutMinutes;
}
return false;
}
}
public void RecordSuccess()
{
_failureCount = 0;
}
public void RecordFailure()
{
_failureCount++;
_lastFailureTime = DateTime.UtcNow;
}
}
}