Smarter approach to the cookies (#103)

* Smarter approach to the cookies

  - Single maxCookieSize = 1400 constant with clear documentation
  - Combined cookie storage for ~40-45% size reduction
  - Backward compatible migration from legacy cookies

* Tuneup the code.
This commit is contained in:
2025-12-12 18:35:06 +00:00
committed by GitHub
parent d0b920c4f0
commit 6efb78b7a8
90 changed files with 1529 additions and 1589 deletions
+14 -25
View File
@@ -18,33 +18,22 @@ const (
// Config provides common configuration for cache backends
type Config struct {
// Type specifies the backend type
Type BackendType
// Memory backend settings
MaxSize int
MaxMemoryBytes int64
CleanupInterval time.Duration
// Redis backend settings
RedisAddr string
RedisPassword string
RedisDB int
RedisPrefix string
PoolSize int
// Hybrid backend settings
L1Config *Config // Memory cache (L1)
L2Config *Config // Redis cache (L2)
AsyncWrites bool // Write to L2 asynchronously
// Resilience settings
L2Config *Config
L1Config *Config
RedisPrefix string
Type BackendType
RedisAddr string
RedisPassword string
PoolSize int
RedisDB int
CleanupInterval time.Duration
MaxMemoryBytes int64
MaxSize int
HealthCheckInterval time.Duration
AsyncWrites bool
EnableCircuitBreaker bool
EnableHealthCheck bool
HealthCheckInterval time.Duration
// Metrics
EnableMetrics bool
EnableMetrics bool
}
// DefaultConfig returns a default configuration for in-memory caching
+18 -28
View File
@@ -13,40 +13,30 @@ import (
// HybridBackend implements a two-tier cache with L1 (memory) and L2 (Redis) backends
// It provides automatic failover, async writes for non-critical data, and optimized read paths
type HybridBackend struct {
primary CacheBackend // L1: Memory cache for fast access
secondary CacheBackend // L2: Redis cache for distributed access
// Configuration
syncWriteCacheTypes map[string]bool // Which cache types require synchronous writes
lastL2Error atomic.Value
secondary CacheBackend
primary CacheBackend
logger Logger
ctx context.Context
syncWriteCacheTypes map[string]bool
asyncWriteBuffer chan *asyncWriteItem
// Metrics
l1Hits atomic.Int64
l2Hits atomic.Int64
misses atomic.Int64
l1Writes atomic.Int64
l2Writes atomic.Int64
errors atomic.Int64
// Fallback tracking
fallbackMode atomic.Bool // True when operating in degraded mode (L1 only)
lastL2Error atomic.Value // Stores last L2 error timestamp
// Lifecycle
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
// Logging
logger Logger
cancel context.CancelFunc
wg sync.WaitGroup
l1Hits atomic.Int64
errors atomic.Int64
l2Writes atomic.Int64
l1Writes atomic.Int64
misses atomic.Int64
l2Hits atomic.Int64
fallbackMode atomic.Bool
}
// asyncWriteItem represents an async write operation
type asyncWriteItem struct {
ctx context.Context
key string
value []byte
ttl time.Duration
ctx context.Context
}
// Logger interface for structured logging
@@ -82,9 +72,9 @@ func (l *defaultLogger) Errorf(format string, args ...interface{}) {
type HybridConfig struct {
Primary CacheBackend
Secondary CacheBackend
SyncWriteCacheTypes map[string]bool // Cache types requiring synchronous L2 writes
AsyncBufferSize int
Logger Logger
SyncWriteCacheTypes map[string]bool
AsyncBufferSize int
}
// NewHybridBackend creates a new hybrid cache backend with L1 (memory) and L2 (Redis) tiers
+6 -6
View File
@@ -17,23 +17,23 @@ import (
// mockBackend is a simple mock implementation of CacheBackend for testing
type mockBackend struct {
pingError error
data map[string]mockEntry
stats map[string]interface{}
mu sync.RWMutex
getCalls atomic.Int32
setCalls atomic.Int32
deleteCalls atomic.Int32
failSet bool
failGet bool
failDelete bool
failClear bool
failPing bool
pingError error
stats map[string]interface{}
getCalls atomic.Int32
setCalls atomic.Int32
deleteCalls atomic.Int32
}
type mockEntry struct {
value []byte
expiresAt time.Time
value []byte
}
// mockBatchBackend extends mockBackend with batch operations
+14 -45
View File
@@ -41,53 +41,22 @@ type CacheBackend interface {
// BackendStats represents statistics for a cache backend
type BackendStats struct {
// Type is the backend type
Type BackendType
// Hits is the number of cache hits
Hits int64
// Misses is the number of cache misses
Misses int64
// Sets is the number of set operations
Sets int64
// Deletes is the number of delete operations
Deletes int64
// Errors is the number of errors
Errors int64
// Evictions is the number of evicted items
Evictions int64
// CurrentSize is the current number of items in cache
CurrentSize int64
// MaxSize is the maximum number of items (0 means unlimited)
MaxSize int64
// MemoryUsage is the approximate memory usage in bytes
MemoryUsage int64
// AverageGetLatency is the average latency for get operations
StartTime time.Time
LastErrorTime time.Time
Type BackendType
LastError string
Deletes int64
Errors int64
Evictions int64
CurrentSize int64
MaxSize int64
MemoryUsage int64
AverageGetLatency time.Duration
// AverageSetLatency is the average latency for set operations
AverageSetLatency time.Duration
// LastError is the last error encountered
LastError string
// LastErrorTime is when the last error occurred
LastErrorTime time.Time
// Uptime is how long the backend has been running
Uptime time.Duration
// StartTime is when the backend was started
StartTime time.Time
Sets int64
Misses int64
Uptime time.Duration
Hits int64
}
// BackendCapabilities describes the capabilities of a cache backend
+27 -35
View File
@@ -11,14 +11,14 @@ import (
// memoryCacheItem represents an item in the memory cache
type memoryCacheItem struct {
key string
value interface{}
expiresAt time.Time
createdAt time.Time
accessedAt time.Time
value interface{}
element *list.Element
key string
accessCount int64
size int64
element *list.Element // for LRU tracking
}
// isExpired checks if the item is expired
@@ -31,39 +31,31 @@ func (item *memoryCacheItem) isExpired() bool {
// MemoryCacheBackend implements the CacheBackend interface using in-memory storage
type MemoryCacheBackend struct {
mu sync.RWMutex
items map[string]*memoryCacheItem
lruList *list.List
maxSize int64
maxMemory int64
currentSize int64
currentMemory int64
// Statistics
hits atomic.Int64
misses atomic.Int64
sets atomic.Int64
deletes atomic.Int64
evictions atomic.Int64
errors atomic.Int64
// Latency tracking
totalGetTime atomic.Int64
totalSetTime atomic.Int64
getCount atomic.Int64
setCount atomic.Int64
// Status
startTime time.Time
lastError string
lastErrorTime time.Time
cleanupTicker *time.Ticker
cleanupDone chan bool
closed atomic.Bool
// Configuration
startTime time.Time
lastErrorTime time.Time
items map[string]*memoryCacheItem
lruList *list.List
cleanupDone chan bool
cleanupTicker *time.Ticker
evictionPolicy string
lastError string
currentMemory int64
misses atomic.Int64
deletes atomic.Int64
evictions atomic.Int64
errors atomic.Int64
totalGetTime atomic.Int64
totalSetTime atomic.Int64
getCount atomic.Int64
setCount atomic.Int64
sets atomic.Int64
hits atomic.Int64
maxSize int64
currentSize int64
maxMemory int64
cleanupInterval time.Duration
evictionPolicy string // "lru", "lfu", "fifo"
mu sync.RWMutex
closed atomic.Bool
}
// NewMemoryCacheBackend creates a new memory cache backend
+10 -16
View File
@@ -9,30 +9,24 @@ import (
// HealthMonitor continuously monitors Redis connection health and triggers reconnections
type HealthMonitor struct {
pool *ConnectionPool
config *HealthMonitorConfig
// State
healthy atomic.Bool
running atomic.Bool
lastCheckTime atomic.Int64 // Unix timestamp
// Metrics
pool *ConnectionPool
config *HealthMonitorConfig
stopChan chan struct{}
wg sync.WaitGroup
lastCheckTime atomic.Int64
consecutiveFailures atomic.Int64
totalChecks atomic.Int64
totalFailures atomic.Int64
// Lifecycle
stopChan chan struct{}
wg sync.WaitGroup
healthy atomic.Bool
running atomic.Bool
}
// HealthMonitorConfig configures the health monitor
type HealthMonitorConfig struct {
CheckInterval time.Duration // How often to check health
Timeout time.Duration // Timeout for health check
UnhealthyThreshold int // Consecutive failures before marking unhealthy
OnHealthChange func(healthy bool)
CheckInterval time.Duration
Timeout time.Duration
UnhealthyThreshold int
}
// DefaultHealthMonitorConfig returns default health monitor configuration
+5 -5
View File
@@ -15,8 +15,8 @@ import (
func TestRESPWriter_WriteCommand(t *testing.T) {
tests := []struct {
name string
args []string
expected string
args []string
}{
{
name: "Simple command",
@@ -205,9 +205,9 @@ func TestRESPReader_ReadInteger(t *testing.T) {
// TestRESPReader_ReadBulkString tests reading bulk strings
func TestRESPReader_ReadBulkString(t *testing.T) {
tests := []struct {
expected interface{}
name string
input string
expected interface{}
wantErr bool
isNil bool
}{
@@ -440,10 +440,10 @@ func TestRESPHelpers(t *testing.T) {
// TestRESPRoundTrip tests full round-trip encoding/decoding
func TestRESPRoundTrip(t *testing.T) {
tests := []struct {
name string
command []string
response string
expected interface{}
name string
response string
command []string
}{
{
name: "PING command",
+28 -40
View File
@@ -33,21 +33,19 @@ type Logger interface {
// Config provides configuration for the cache
type Config struct {
Logger Logger
JWKConfig *JWKConfig
MetadataConfig *MetadataConfig
TokenConfig *TokenConfig
Type Type
MaxSize int
MaxMemoryBytes int64
DefaultTTL time.Duration
CleanupInterval time.Duration
EnableCompression bool
MaxMemoryBytes int64
MaxSize int
EnableMetrics bool
EnableAutoCleanup bool
EnableMemoryLimit bool
Logger Logger
// Type-specific configurations
TokenConfig *TokenConfig
MetadataConfig *MetadataConfig
JWKConfig *JWKConfig
EnableCompression bool
}
// TokenConfig provides token-specific cache configuration
@@ -59,11 +57,11 @@ type TokenConfig struct {
// MetadataConfig provides metadata-specific cache configuration
type MetadataConfig struct {
SecurityCriticalFields []string
GracePeriod time.Duration
ExtendedGracePeriod time.Duration
MaxGracePeriod time.Duration
SecurityCriticalMaxGracePeriod time.Duration
SecurityCriticalFields []string
}
// JWKConfig provides JWK-specific cache configuration
@@ -75,45 +73,35 @@ type JWKConfig struct {
// Item represents a single cache entry
type Item struct {
Key string
Value interface{}
Size int64
ExpiresAt time.Time
LastAccessed time.Time
AccessCount int64
Value interface{}
Metadata map[string]interface{}
element *list.Element
Key string
CacheType Type
// Type-specific metadata
Metadata map[string]interface{}
// LRU list element reference
element *list.Element
Size int64
AccessCount int64
}
// Cache provides a single, unified cache implementation
type Cache struct {
mu sync.RWMutex
items map[string]*Item
lruList *list.List
config Config
logger Logger
// Memory management
config Config
ctx context.Context
logger Logger
cancel context.CancelFunc
lruList *list.List
items map[string]*Item
stopCleanup chan bool
wg sync.WaitGroup
currentSize int64
currentMemory int64
// Metrics
hits int64
misses int64
evictions int64
sets int64
// Lifecycle management
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
stopCleanup chan bool
closed int32
hits int64
misses int64
evictions int64
sets int64
mu sync.RWMutex
closed int32
}
// DefaultConfig returns a default cache configuration
+11 -11
View File
@@ -1750,19 +1750,19 @@ func TestAdvancedEdgeCases(t *testing.T) {
// Test with various data types
testCases := []struct {
key string
value interface{}
key string
}{
{"string", "test string"},
{"int", 42},
{"float", 3.14159},
{"bool", true},
{"slice", []string{"a", "b", "c"}},
{"map", map[string]int{"one": 1, "two": 2}},
{"nil", nil},
{"empty-string", ""},
{"empty-slice", []string{}},
{"empty-map", map[string]interface{}{}},
{key: "string", value: "test string"},
{key: "int", value: 42},
{key: "float", value: 3.14159},
{key: "bool", value: true},
{key: "slice", value: []string{"a", "b", "c"}},
{key: "map", value: map[string]int{"one": 1, "two": 2}},
{key: "nil", value: nil},
{key: "empty-string", value: ""},
{key: "empty-slice", value: []string{}},
{key: "empty-map", value: map[string]interface{}{}},
}
for _, tc := range testCases {
+2 -7
View File
@@ -7,22 +7,17 @@ import (
// Manager manages multiple cache instances with singleton pattern
type Manager struct {
mu sync.RWMutex
// Core caches
logger Logger
tokenCache *Cache
metadataCache *Cache
jwkCache *Cache
sessionCache *Cache
generalCache *Cache
// Typed wrappers
typedToken *TokenCache
typedMetadata *MetadataCache
typedJWK *JWKCache
typedSession *SessionCache
logger Logger
mu sync.RWMutex
}
var (
+23 -42
View File
@@ -48,23 +48,12 @@ func (s State) String() string {
// CircuitBreakerConfig holds configuration for the circuit breaker
type CircuitBreakerConfig struct {
// MaxFailures is the number of consecutive failures before opening the circuit
MaxFailures int
// FailureThreshold is the failure rate threshold (0.0 to 1.0)
FailureThreshold float64
// Timeout is how long the circuit stays open before trying half-open
Timeout time.Duration
// HalfOpenMaxRequests is the number of requests allowed in half-open state
OnStateChange func(from, to State)
MaxFailures int
FailureThreshold float64
Timeout time.Duration
HalfOpenMaxRequests int
// ResetTimeout is how long to wait before resetting counters in closed state
ResetTimeout time.Duration
// OnStateChange is called when the circuit breaker changes state
OnStateChange func(from, to State)
ResetTimeout time.Duration
}
// DefaultCircuitBreakerConfig returns default configuration
@@ -80,28 +69,20 @@ func DefaultCircuitBreakerConfig() *CircuitBreakerConfig {
// CircuitBreaker implements the circuit breaker pattern
type CircuitBreaker struct {
config *CircuitBreakerConfig
// State management
state atomic.Int32
lastStateChange time.Time
stateMu sync.RWMutex
// Failure tracking
consecutiveFailures atomic.Int32
totalRequests atomic.Int64
nextRetryTime time.Time
lastStateChange time.Time
lastSuccessTime time.Time
lastFailureTime time.Time
config *CircuitBreakerConfig
totalFailures atomic.Int64
totalRequests atomic.Int64
stateTransitions atomic.Int64
rejectedRequests atomic.Int64
stateMu sync.RWMutex
timeMu sync.RWMutex
halfOpenRequests atomic.Int32
// Timing
lastFailureTime time.Time
lastSuccessTime time.Time
nextRetryTime time.Time
timeMu sync.RWMutex
// Metrics
stateTransitions atomic.Int64
rejectedRequests atomic.Int64
consecutiveFailures atomic.Int32
state atomic.Int32
}
// NewCircuitBreaker creates a new circuit breaker
@@ -313,17 +294,17 @@ func (cb *CircuitBreaker) Stats() CircuitBreakerStats {
// CircuitBreakerStats holds statistics for the circuit breaker
type CircuitBreakerStats struct {
State State
ConsecutiveFailures int32
LastFailureTime time.Time
LastSuccessTime time.Time
LastStateChange time.Time
NextRetryTime time.Time
TotalRequests int64
TotalFailures int64
SuccessRate float64
RejectedRequests int64
StateTransitions int64
LastFailureTime time.Time
LastSuccessTime time.Time
LastStateChange time.Time
NextRetryTime time.Time
State State
ConsecutiveFailures int32
}
// IsHealthy returns true if the circuit breaker is in a healthy state
+1 -1
View File
@@ -28,8 +28,8 @@ type mockBackend struct {
}
type mockEntry struct {
value []byte
expiresAt time.Time
value []byte
}
func newMockBackend() *mockBackend {
+28 -49
View File
@@ -41,26 +41,13 @@ func (h HealthStatus) String() string {
// HealthCheckConfig holds configuration for the health checker
type HealthCheckConfig struct {
// CheckInterval is how often to check health
CheckInterval time.Duration
// Timeout is the timeout for each health check
Timeout time.Duration
// HealthyThreshold is the number of consecutive successes to become healthy
HealthyThreshold int
// UnhealthyThreshold is the number of consecutive failures to become unhealthy
OnStatusChange func(from, to HealthStatus)
CheckFunc func(ctx context.Context) error
CheckInterval time.Duration
Timeout time.Duration
HealthyThreshold int
UnhealthyThreshold int
// DegradedThreshold is the latency threshold in ms to mark as degraded
DegradedThreshold time.Duration
// OnStatusChange is called when health status changes
OnStatusChange func(from, to HealthStatus)
// CheckFunc is the function to check health
CheckFunc func(ctx context.Context) error
DegradedThreshold time.Duration
}
// DefaultHealthCheckConfig returns default configuration
@@ -76,31 +63,23 @@ func DefaultHealthCheckConfig() *HealthCheckConfig {
// HealthChecker monitors the health of a backend
type HealthChecker struct {
config *HealthCheckConfig
// Status tracking
status atomic.Int32
consecutiveSuccesses atomic.Int32
lastCheckTime time.Time
lastSuccessTime time.Time
lastFailureTime time.Time
config *HealthCheckConfig
stopChan chan struct{}
ticker *time.Ticker
wg sync.WaitGroup
statusChanges atomic.Int64
totalChecks atomic.Int64
totalSuccesses atomic.Int64
totalFailures atomic.Int64
averageLatency atomic.Int64
timeMu sync.RWMutex
consecutiveFailures atomic.Int32
// Timing
lastCheckTime time.Time
lastSuccessTime time.Time
lastFailureTime time.Time
averageLatency atomic.Int64
timeMu sync.RWMutex
// Metrics
totalChecks atomic.Int64
totalSuccesses atomic.Int64
totalFailures atomic.Int64
statusChanges atomic.Int64
// Lifecycle
ticker *time.Ticker
stopChan chan struct{}
stopped atomic.Bool
wg sync.WaitGroup
consecutiveSuccesses atomic.Int32
stopped atomic.Bool
status atomic.Int32
}
// NewHealthChecker creates a new health checker
@@ -342,19 +321,19 @@ func (hc *HealthChecker) Stats() HealthCheckerStats {
// HealthCheckerStats holds statistics for the health checker
type HealthCheckerStats struct {
Status HealthStatus
ConsecutiveSuccesses int32
ConsecutiveFailures int32
LastCheckTime time.Time
LastFailureTime time.Time
LastSuccessTime time.Time
TotalChecks int64
TotalSuccesses int64
TotalFailures int64
SuccessRate float64
AverageLatency time.Duration
StatusChanges int64
LastCheckTime time.Time
LastSuccessTime time.Time
LastFailureTime time.Time
HealthScore float64
Status HealthStatus
ConsecutiveFailures int32
ConsecutiveSuccesses int32
}
// Reset resets the health checker statistics
+7 -11
View File
@@ -12,20 +12,16 @@ import (
// HealthCheckBackend wraps a cache backend with health checking
type HealthCheckBackend struct {
backend backends.CacheBackend
config *HealthCheckConfig
// Health tracking
lastCheck time.Time
backend backends.CacheBackend
ctx context.Context
config *HealthCheckConfig
cancel context.CancelFunc
wg sync.WaitGroup
checkMutex sync.RWMutex
status atomic.Int32
consecutiveFails atomic.Int32
consecutiveOK atomic.Int32
lastCheck time.Time
checkMutex sync.RWMutex
// Lifecycle
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
}
// NewHealthCheckBackend creates a new health check wrapped backend
+2 -2
View File
@@ -292,12 +292,12 @@ type SessionCache struct {
// SessionData represents session information
type SessionData struct {
ExpiresAt time.Time `json:"expires_at"`
Claims map[string]interface{} `json:"claims"`
ID string `json:"id"`
UserID string `json:"user_id"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresAt time.Time `json:"expires_at"`
Claims map[string]interface{} `json:"claims"`
}
// NewSessionCache creates a new session cache
+1 -1
View File
@@ -11,10 +11,10 @@ import (
// Mock logger for testing
type mockLogger struct {
mu sync.Mutex
logs []string
errLogs []string
debugLog []string
mu sync.Mutex
}
func (m *mockLogger) Logf(format string, args ...interface{}) {
+11 -11
View File
@@ -19,20 +19,20 @@ type Logger interface {
// BackgroundTask represents a recurring background task
type BackgroundTask struct {
name string
interval time.Duration
taskFunc func()
lastRun time.Time
logger Logger
ctx context.Context
ticker *time.Ticker
stopChan chan bool
isRunning int32
logger Logger
waitGroup *sync.WaitGroup
lastRun time.Time
taskFunc func()
cancelFunc context.CancelFunc
name string
runCount int64
errorCount int64
interval time.Duration
mu sync.RWMutex
ctx context.Context
cancelFunc context.CancelFunc
isRunning int32
}
// NewBackgroundTask creates a new background task
@@ -183,11 +183,11 @@ func (bt *BackgroundTask) IsRunning() bool {
// TaskRegistry manages all background tasks
type TaskRegistry struct {
tasks map[string]*BackgroundTask
mu sync.RWMutex
logger Logger
maxTasks int
tasks map[string]*BackgroundTask
circuitBreaker *TaskCircuitBreaker
maxTasks int
mu sync.RWMutex
}
// globalTaskRegistry is the singleton task registry
+13 -13
View File
@@ -11,14 +11,14 @@ import (
// TaskCircuitBreaker prevents task creation failures from cascading
type TaskCircuitBreaker struct {
lastFailureTime time.Time
logger Logger
taskFailures map[string]int32
timeout time.Duration
mu sync.RWMutex
failureThreshold int32
failureCount int32
lastFailureTime time.Time
timeout time.Duration
state int32 // 0: closed, 1: open
logger Logger
mu sync.RWMutex
taskFailures map[string]int32
state int32
}
// CircuitBreakerState represents the state of the circuit breaker
@@ -140,14 +140,14 @@ func (cb *TaskCircuitBreaker) GetState() CircuitBreakerState {
// TaskMemoryMonitor monitors memory usage and can trigger cleanup
type TaskMemoryMonitor struct {
lastCheck time.Time
logger Logger
registry *TaskRegistry
stopChan chan bool
memoryThreshold uint64
checkInterval time.Duration
isMonitoring int32
stopChan chan bool
lastCheck time.Time
mu sync.RWMutex
isMonitoring int32
}
var (
@@ -310,13 +310,13 @@ func (tmm *TaskMemoryMonitor) GetStats() map[string]interface{} {
// WorkerPool manages a pool of worker goroutines for task execution
type WorkerPool struct {
workers int
taskQueue chan func()
workerWg sync.WaitGroup
isRunning int32
logger Logger
taskQueue chan func()
stopChan chan bool
metrics WorkerPoolMetrics
workerWg sync.WaitGroup
workers int
isRunning int32
}
// WorkerPoolMetrics tracks worker pool performance
+2 -2
View File
@@ -12,9 +12,9 @@ import (
type FeatureFlag struct {
name string
description string
enabled atomic.Bool
mu sync.RWMutex
callbacks []func(bool)
mu sync.RWMutex
enabled atomic.Bool
}
// FeatureManager manages all feature flags in the application
+19 -28
View File
@@ -14,50 +14,41 @@ import (
// and resource leaks. It provides centralized management of HTTP client transports with
// proper lifecycle management and security controls.
type TransportPool struct {
mu sync.RWMutex
transports map[string]*sharedTransport
maxConns int
ctx context.Context
transports map[string]*sharedTransport
cancel context.CancelFunc
clientCount int32 // Track total HTTP clients
maxClients int32 // Limit total clients
maxConns int
mu sync.RWMutex
clientCount int32
maxClients int32
}
// sharedTransport wraps an HTTP transport with reference counting
type sharedTransport struct {
transport *http.Transport
refCount int32
lastUsed time.Time
transport *http.Transport
config TransportConfig
refCount int32
}
// TransportConfig defines configuration for HTTP transports
type TransportConfig struct {
// Timeouts
DialTimeout time.Duration
TLSHandshakeTimeout time.Duration
MaxConnsPerHost int
WriteBufferSize int
ResponseHeaderTimeout time.Duration
ExpectContinueTimeout time.Duration
IdleConnTimeout time.Duration
KeepAlive time.Duration
// Connection limits
MaxIdleConns int
MaxIdleConnsPerHost int
MaxConnsPerHost int
// Features
ForceHTTP2 bool
DisableKeepAlives bool
DisableCompression bool
// Buffer sizes
WriteBufferSize int
ReadBufferSize int
// TLS
InsecureSkipVerify bool
MinTLSVersion uint16
TLSHandshakeTimeout time.Duration
MaxIdleConns int
DialTimeout time.Duration
MaxIdleConnsPerHost int
ReadBufferSize int
MinTLSVersion uint16
ForceHTTP2 bool
DisableCompression bool
InsecureSkipVerify bool
DisableKeepAlives bool
}
var (
+4 -4
View File
@@ -154,10 +154,10 @@ func TestAzureProvider_ValidateTokens(t *testing.T) {
provider := NewAzureProvider()
tests := []struct {
name string
session *mockSession
verifierError error
session *mockSession
cacheData map[string]interface{}
name string
expectedResult ValidationResult
}{
{
@@ -369,9 +369,9 @@ func TestAzureProvider_OfflineAccessHandling(t *testing.T) {
tests := []struct {
name string
inputScopes []string
expectedCount int // Expected number of offline_access scopes (should be 1)
description string
inputScopes []string
expectedCount int
}{
{
name: "No offline_access - should add one",
+5 -5
View File
@@ -8,10 +8,10 @@ import (
// Mock implementations for testing
type mockSession struct {
authenticated bool
idToken string
accessToken string
refreshToken string
authenticated bool
}
func (s *mockSession) GetIDToken() string { return s.idToken }
@@ -338,10 +338,10 @@ func TestBaseProvider_ValidateTokenExpiry(t *testing.T) {
gracePeriod := 5 * time.Minute
tests := []struct {
name string
claims map[string]interface{}
cacheFound bool
name string
expectedResult ValidationResult
cacheFound bool
}{
{
name: "Token not found in cache, has refresh token",
@@ -438,10 +438,10 @@ func TestBaseProvider_ValidateTokenExpiry_NoRefreshToken(t *testing.T) {
gracePeriod := 5 * time.Minute
tests := []struct {
name string
claims map[string]interface{}
cacheFound bool
name string
expectedResult ValidationResult
cacheFound bool
}{
{
name: "Token not found in cache, no refresh token",
+2 -2
View File
@@ -25,9 +25,9 @@ func TestProviderFactory_CreateProvider(t *testing.T) {
tests := []struct {
name string
issuerURL string
errMsg string
expectedType ProviderType
wantErr bool
errMsg string
}{
{
name: "Google provider",
@@ -158,10 +158,10 @@ func TestProviderFactory_CreateProviderByType(t *testing.T) {
tests := []struct {
name string
errMsg string
providerType ProviderType
expectedType ProviderType
wantErr bool
errMsg string
}{
{
name: "Generic provider",
+2 -2
View File
@@ -136,9 +136,9 @@ func TestGenericProvider_ValidateTokens(t *testing.T) {
provider := NewGenericProvider()
tests := []struct {
name string
session *mockSession
verifierError error
session *mockSession
name string
expectedResult ValidationResult
}{
{
+1 -1
View File
@@ -172,8 +172,8 @@ func TestGoogleProvider_OfflineAccessFiltering(t *testing.T) {
tests := []struct {
name string
inputScopes []string
description string
inputScopes []string
}{
{
name: "Multiple offline_access occurrences",
+3 -3
View File
@@ -82,9 +82,9 @@ func TestProviderRegistry_GetProviderByType(t *testing.T) {
registry.RegisterProvider(googleProvider)
tests := []struct {
expected OIDCProvider
name string
providerType ProviderType
expected OIDCProvider
}{
{
name: "Get Generic provider",
@@ -180,9 +180,9 @@ func TestProviderRegistry_DetectProvider(t *testing.T) {
registry.RegisterProvider(gitlabProvider)
tests := []struct {
expected OIDCProvider
name string
issuerURL string
expected OIDCProvider
}{
{
name: "Google provider detection",
@@ -640,9 +640,9 @@ func TestProviderRegistry_GitLabDetection_RealWorldURLs(t *testing.T) {
registry.RegisterProvider(githubProvider)
realWorldTests := []struct {
expected OIDCProvider
name string
issuerURL string
expected OIDCProvider
}{
// Actual self-hosted GitLab examples from issue #61
{
+9 -9
View File
@@ -20,8 +20,8 @@ func TestValidateIssuerURL(t *testing.T) {
tests := []struct {
name string
issuerURL string
wantErr bool
errMsg string
wantErr bool
}{
{
name: "valid https URL",
@@ -106,8 +106,8 @@ func TestValidateClientID(t *testing.T) {
tests := []struct {
name string
clientID string
wantErr bool
errMsg string
wantErr bool
}{
{
name: "valid client ID",
@@ -173,9 +173,9 @@ func TestValidateClientID(t *testing.T) {
func TestValidateScopes(t *testing.T) {
tests := []struct {
name string
errMsg string
scopes []string
wantErr bool
errMsg string
}{
{
name: "valid scopes with openid",
@@ -248,8 +248,8 @@ func TestValidateRedirectURL(t *testing.T) {
tests := []struct {
name string
redirectURL string
wantErr bool
errMsg string
wantErr bool
}{
{
name: "valid https redirect URL",
@@ -315,11 +315,11 @@ func TestValidateRedirectURL(t *testing.T) {
// TestValidateProviderSpecificConfig tests provider-specific configuration validation
func TestValidateProviderSpecificConfig(t *testing.T) {
tests := []struct {
name string
provider OIDCProvider
config map[string]interface{}
wantErr bool
name string
errMsg string
wantErr bool
}{
{
name: "valid Google config",
@@ -458,8 +458,8 @@ func TestValidateGoogleConfig_EdgeCases(t *testing.T) {
googleProvider := NewGoogleProvider()
tests := []struct {
name string
config map[string]interface{}
name string
wantErr bool
}{
{
@@ -502,10 +502,10 @@ func TestValidateAzureConfig_EdgeCases(t *testing.T) {
azureProvider := NewAzureProvider()
tests := []struct {
name string
config map[string]interface{}
wantErr bool
name string
errMsg string
wantErr bool
}{
{
name: "valid tenant ID format",
+2 -2
View File
@@ -7,9 +7,9 @@ import (
// ProviderWarning represents a warning about provider limitations or requirements.
type ProviderWarning struct {
ProviderType ProviderType
Level string // "info", "warning", "error"
Level string
Message string
ProviderType ProviderType
}
// GetProviderWarnings returns warnings about provider-specific limitations.
+1 -1
View File
@@ -9,9 +9,9 @@ import (
func TestGetProviderWarnings(t *testing.T) {
tests := []struct {
name string
checkContent string
providerType ProviderType
expectCount int
checkContent string
}{
{
name: "GitHub has OAuth 2.0 warning",
+8 -14
View File
@@ -34,21 +34,15 @@ type Logger interface {
// for all recovery mechanism implementations. It handles request counting,
// success/failure tracking, and timestamp management in a thread-safe manner.
type BaseRecoveryMechanism struct {
// name identifies the recovery mechanism instance
name string
// logger provides structured logging capabilities
logger Logger
// Metrics tracked with atomic operations for thread safety
logger Logger
name string
lastSuccessStr string
lastFailureStr string
totalRequests int64
successCount int64
failureCount int64
lastSuccessStr string
lastFailureStr string
// mutexes for thread-safe timestamp updates
successMutex sync.RWMutex
failureMutex sync.RWMutex
successMutex sync.RWMutex
failureMutex sync.RWMutex
}
// NewBaseRecoveryMechanism creates a new base recovery mechanism with the given name and logger.
@@ -182,10 +176,10 @@ const (
// HTTPError represents an HTTP error with status code and message
type HTTPError struct {
StatusCode int
Headers map[string]string
Message string
Body []byte
Headers map[string]string
StatusCode int
}
// Error implements the error interface
+7 -13
View File
@@ -60,20 +60,14 @@ func DefaultCircuitBreakerConfig() CircuitBreakerConfig {
// CircuitBreaker implements the circuit breaker pattern for fault tolerance.
// It prevents cascading failures by temporarily blocking requests to a failing service.
type CircuitBreaker struct {
*BaseRecoveryMechanism
config CircuitBreakerConfig
// State management
state int32 // atomic: CircuitBreakerState
lastStateChange time.Time
stateMutex sync.RWMutex
// Failure tracking
consecutiveFailures int32 // atomic
consecutiveSuccesses int32 // atomic
// Half-open state management
halfOpenRequests int32 // atomic
*BaseRecoveryMechanism
config CircuitBreakerConfig
stateMutex sync.RWMutex
state int32
consecutiveFailures int32
consecutiveSuccesses int32
halfOpenRequests int32
}
// NewCircuitBreaker creates a new circuit breaker with the given configuration
+8 -17
View File
@@ -15,20 +15,13 @@ import (
// RetryConfig defines configuration for the retry executor
type RetryConfig struct {
// MaxAttempts is the maximum number of retry attempts
MaxAttempts int
// InitialDelay is the initial delay between retries
InitialDelay time.Duration
// MaxDelay is the maximum delay between retries
MaxDelay time.Duration
// Multiplier is the backoff multiplier
Multiplier float64
// RandomizationFactor adds jitter to delays (0.0 to 1.0)
RandomizationFactor float64
// RetryableErrors defines which errors should trigger a retry
RetryableErrors []string
// RetryableStatusCodes defines which HTTP status codes should trigger a retry
RetryableErrors []string
RetryableStatusCodes []int
MaxAttempts int
InitialDelay time.Duration
MaxDelay time.Duration
Multiplier float64
RandomizationFactor float64
}
// DefaultRetryConfig returns sensible default retry configuration
@@ -46,13 +39,11 @@ func DefaultRetryConfig() RetryConfig {
// RetryExecutor implements retry logic with exponential backoff
type RetryExecutor struct {
lastRetryTime time.Time
*BaseRecoveryMechanism
config RetryConfig
// Metrics
config RetryConfig
totalRetries int64
maxRetriesHit int64
lastRetryTime time.Time
retryTimeMutex sync.RWMutex
}
+8 -8
View File
@@ -273,17 +273,17 @@ func TestRetryExecutor_isRetryableError(t *testing.T) {
executor := NewRetryExecutor(config, logger)
tests := []struct {
name string
err error
name string
expected bool
}{
{"nil error", nil, false},
{"connection refused", errors.New("connection refused"), true},
{"timeout", errors.New("TIMEOUT"), true}, // case insensitive
{"EOF", errors.New("EOF"), false},
{"random error", errors.New("something else"), false},
{"context cancelled", context.Canceled, false},
{"context deadline exceeded", context.DeadlineExceeded, false},
{name: "nil error", err: nil, expected: false},
{name: "connection refused", err: errors.New("connection refused"), expected: true},
{name: "timeout", err: errors.New("TIMEOUT"), expected: true}, // case insensitive
{name: "EOF", err: errors.New("EOF"), expected: false},
{name: "random error", err: errors.New("something else"), expected: false},
{name: "context cancelled", err: context.Canceled, expected: false},
{name: "context deadline exceeded", err: context.DeadlineExceeded, expected: false},
}
for _, tt := range tests {
+6 -6
View File
@@ -13,10 +13,10 @@ import (
// Mock logger for testing
type mockLogger struct {
mu sync.Mutex
logs []string
errLogs []string
debugLog []string
mu sync.Mutex
}
func (m *mockLogger) Logf(format string, args ...interface{}) {
@@ -202,13 +202,13 @@ func TestBaseRecoveryMechanism_ConcurrentAccess(t *testing.T) {
// CircuitBreakerState tests
func TestCircuitBreakerState_String(t *testing.T) {
tests := []struct {
state CircuitBreakerState
expected string
state CircuitBreakerState
}{
{CircuitBreakerClosed, "closed"},
{CircuitBreakerOpen, "open"},
{CircuitBreakerHalfOpen, "half-open"},
{CircuitBreakerState(99), "unknown"},
{state: CircuitBreakerClosed, expected: "closed"},
{state: CircuitBreakerOpen, expected: "open"},
{state: CircuitBreakerHalfOpen, expected: "half-open"},
{state: CircuitBreakerState(99), expected: "unknown"},
}
for _, tt := range tests {
+5 -5
View File
@@ -29,26 +29,26 @@ type JWK struct {
type TokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token,omitempty"`
IDToken string `json:"id_token,omitempty"`
Scope string `json:"scope,omitempty"`
ExpiresIn int `json:"expires_in"`
}
// IntrospectionResponse represents a token introspection response
type IntrospectionResponse struct {
Active bool `json:"active"`
Scope string `json:"scope,omitempty"`
ClientID string `json:"client_id,omitempty"`
Username string `json:"username,omitempty"`
TokenType string `json:"token_type,omitempty"`
Exp int64 `json:"exp,omitempty"`
Iat int64 `json:"iat,omitempty"`
Nbf int64 `json:"nbf,omitempty"`
Sub string `json:"sub,omitempty"`
Aud string `json:"aud,omitempty"`
Iss string `json:"iss,omitempty"`
Jti string `json:"jti,omitempty"`
Exp int64 `json:"exp,omitempty"`
Iat int64 `json:"iat,omitempty"`
Nbf int64 `json:"nbf,omitempty"`
Active bool `json:"active"`
}
// JWKCache is a testify mock for JWK caching operations
+2 -2
View File
@@ -8,16 +8,16 @@ import (
// SessionData represents session data for testing
type SessionData struct {
Claims map[string]interface{}
Email string
AccessToken string
RefreshToken string
IDToken string
Expiry int64
Nonce string
State string
CodeVerifier string
RedirectURL string
Claims map[string]interface{}
Expiry int64
}
// SessionManager is a testify mock for session management
+24 -39
View File
@@ -16,45 +16,30 @@ import (
// OIDCServerConfig configures the mock OIDC server behavior
type OIDCServerConfig struct {
// Identity
Issuer string
// Discovery
ScopesSupported []string
ResponseTypesSupported []string
JWKSResponse map[string]interface{}
TokenFixture *fixtures.TokenFixture
UserinfoError *OIDCError
UserinfoResponse map[string]interface{}
IntrospectionResponse map[string]interface{}
JWKSError *OIDCError
RefreshError *OIDCError
TokenResponse map[string]interface{}
TokenError *OIDCError
IntrospectionError *OIDCError
RefreshResponse map[string]interface{}
Issuer string
GrantTypesSupported []string
ClaimsSupported []string
TokenEndpointAuthMethods []string
// Token fixture for signing
TokenFixture *fixtures.TokenFixture
// Token endpoint behavior
TokenResponse map[string]interface{}
TokenError *OIDCError
TokenDelay time.Duration
RefreshResponse map[string]interface{}
RefreshError *OIDCError
// JWKS behavior
JWKSResponse map[string]interface{}
JWKSError *OIDCError
JWKSDelay time.Duration
// Introspection behavior
IntrospectionResponse map[string]interface{}
IntrospectionError *OIDCError
// Userinfo behavior
UserinfoResponse map[string]interface{}
UserinfoError *OIDCError
// Simulation flags
SimulateTimeout bool
TimeoutDuration time.Duration
RateLimitAfter int
FailAfterN int
FailWithStatus int
ScopesSupported []string
ClaimsSupported []string
ResponseTypesSupported []string
FailAfterN int
JWKSDelay time.Duration
TimeoutDuration time.Duration
RateLimitAfter int
TokenDelay time.Duration
FailWithStatus int
SimulateTimeout bool
}
// OIDCError represents an OAuth error response
@@ -67,9 +52,9 @@ type OIDCError struct {
type OIDCServer struct {
*httptest.Server
Config *OIDCServerConfig
RequestCount int32
mu sync.Mutex
requests []*http.Request
mu sync.Mutex
RequestCount int32
}
// NewOIDCServer creates a new mock OIDC server
+4 -4
View File
@@ -135,9 +135,9 @@ func TestIsTestMode(t *testing.T) {
// We'll test what we can control via environment variables.
tests := []struct {
name string
setup func()
cleanup func()
name string
expected bool
}{
{
@@ -206,8 +206,8 @@ func TestIsTestMode(t *testing.T) {
func TestIsTestModeEdgeCases(t *testing.T) {
// Test with various environment variable combinations
tests := []struct {
name string
env map[string]string
name string
}{
{
name: "all env vars empty",
@@ -560,11 +560,11 @@ func TestIsTestModeYaegiCompiler(t *testing.T) {
// mockLogger is a simple mock implementation for testing
type mockLogger struct {
lastFormat string
lastArgs []interface{}
infoCalls int
debugCalls int
errorCalls int
lastFormat string
lastArgs []interface{}
}
func (m *mockLogger) Infof(format string, args ...interface{}) {