reverseproxy

package module
v1.3.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 24, 2025 License: MIT Imports: 27 Imported by: 0

README

Reverse Proxy Module

Go Reference

A module for the Modular framework that provides a flexible reverse proxy with advanced routing capabilities.

Overview

The Reverse Proxy module functions as a versatile API gateway that can route requests to multiple backend services, combine responses, and support tenant-specific routing configurations. It's designed to be flexible, extensible, and easily configurable.

Key Features

  • Multi-Backend Routing: Route HTTP requests to any number of configurable backend services
  • Per-Backend Configuration: Configure path rewriting and header rewriting for each backend service
  • Per-Endpoint Configuration: Override backend configuration for specific endpoints within a backend
  • Feature Flag Support: Control backend and route behavior using feature flags with optional alternatives
  • Hostname Handling: Control how the Host header is handled (preserve original, use backend, or use custom)
  • Header Rewriting: Add, modify, or remove headers before forwarding requests
  • Path Rewriting: Transform request paths before forwarding to backends
  • Response Aggregation: Combine responses from multiple backends using various strategies
  • Custom Response Transformers: Create custom functions to transform and merge backend responses
  • Tenant Awareness: Support for multi-tenant environments with tenant-specific routing
  • Pattern-Based Routing: Direct requests to specific backends based on URL patterns
  • Custom Endpoint Mapping: Define flexible mappings from frontend endpoints to backend services
  • Health Checking: Continuous monitoring of backend service availability with DNS resolution and HTTP checks
  • Circuit Breaker: Automatic failure detection and recovery with configurable thresholds
  • Response Caching: Performance optimization with TTL-based caching
  • Metrics Collection: Comprehensive metrics for monitoring and debugging
  • Dry Run Mode: Compare responses between different backends for testing and validation

Installation

go get github.com/CrisisTextLine/modular/modules/reverseproxy@v1.0.0

Documentation

Usage

package main

import (
	"github.com/CrisisTextLine/modular"
	"github.com/CrisisTextLine/modular/modules/chimux"
	"github.com/CrisisTextLine/modular/modules/reverseproxy"
	"log/slog"
	"os"
)

func main() {
	// Create a new application
	app := modular.NewStdApplication(
		modular.NewStdConfigProvider(&AppConfig{}),
		slog.New(slog.NewTextHandler(os.Stdout, nil)),
	)

	// Register required modules
	app.RegisterModule(chimux.NewChiMuxModule())
	
	// Register the reverseproxy module
	proxyModule, err := reverseproxy.NewModule()
	if err != nil {
		app.Logger().Error("Failed to create reverseproxy module", "error", err)
		os.Exit(1)
	}
	app.RegisterModule(proxyModule)

	// Run the application
	if err := app.Run(); err != nil {
		app.Logger().Error("Application error", "error", err)
		os.Exit(1)
	}
}

Configuration

Basic Configuration
# config.yaml
reverseproxy:
  # Define your backend services
  backend_services:
    api: "http://api.example.com"
    auth: "http://auth.example.com"
    user: "http://user-service.example.com"
  
  # Set the default backend
  default_backend: "api"
  
  # Tenant-specific configuration
  tenant_id_header: "X-Tenant-ID"
  require_tenant_id: false
  
  # Health check configuration
  health_check:
    enabled: true
    interval: "30s"
    timeout: "5s"
    recent_request_threshold: "60s"
    expected_status_codes: [200, 204]
    health_endpoints:
      api: "/health"
      auth: "/api/health"
    backend_health_check_config:
      api:
        enabled: true
        interval: "15s"
        timeout: "3s"
        expected_status_codes: [200]
      auth:
        enabled: true
        endpoint: "/status"
        interval: "45s"
        timeout: "10s"
        expected_status_codes: [200, 201]

  # Per-backend configuration
  backend_configs:
    api:
      path_rewriting:
        strip_base_path: "/api/v1"
        base_path_rewrite: "/internal/api"
      header_rewriting:
        hostname_handling: "preserve_original"
        set_headers:
          X-API-Key: "secret-key"
          X-Service: "api"
      
      endpoints:
        users:
          pattern: "/users/*"
          path_rewriting:
            base_path_rewrite: "/internal/users"
          header_rewriting:
            hostname_handling: "use_custom"
            custom_hostname: "users.internal.com"
    
    auth:
      header_rewriting:
        hostname_handling: "use_backend"
        set_headers:
          X-Service: "auth"


  # Composite routes for response aggregation
  composite_routes:
    "/api/user/profile":
      pattern: "/api/user/profile"
      backends: ["user", "api"]
      strategy: "merge"
Advanced Features

The module supports several advanced features:

  1. Custom Response Transformers: Create custom functions to transform responses from multiple backends
  2. Custom Endpoint Mappings: Define detailed mappings between frontend endpoints and backend services
  3. Tenant-Specific Routing: Route requests to different backend URLs based on tenant ID
  4. Health Checking: Continuous monitoring of backend service availability with configurable endpoints and intervals
  5. Circuit Breaker: Automatic failure detection and recovery to prevent cascading failures
  6. Response Caching: Performance optimization with TTL-based caching of responses
  7. Feature Flags: Control backend and route behavior dynamically using feature flag evaluation
Feature Flag Support

The reverse proxy module supports feature flags to control routing behavior dynamically. Feature flags can be used to:

  • Enable/disable specific backends
  • Route to alternative backends when features are disabled
  • Control composite route availability
  • Support A/B testing and gradual rollouts
  • Provide tenant-specific feature access
Feature Flag Configuration
reverseproxy:
  # Backend configurations with feature flags
  backend_configs:
    api-v2:
      feature_flag_id: "api-v2-enabled"     # Feature flag to check
      alternative_backend: "api-v1"         # Fallback when disabled
    
    beta-features:
      feature_flag_id: "beta-features"
      alternative_backend: "stable-api"
  
  # Composite routes with feature flags
  composite_routes:
    "/api/enhanced":
      backends: ["api-v2", "analytics"]
      strategy: "merge"
      feature_flag_id: "enhanced-api"       # Feature flag for composite route
      alternative_backend: "api-v1"         # Single backend fallback
Feature Flag Evaluator Service

The reverse proxy module uses an aggregator pattern for feature flag evaluation, allowing multiple evaluators to work together with priority-based ordering:

Built-in File Evaluator: Automatically available using tenant-aware configuration (lowest priority, fallback).

External Evaluators: Register additional evaluators by implementing the FeatureFlagEvaluator interface. The service name doesn't matter for discovery - the aggregator finds evaluators by interface matching:

// Register a remote feature flag service
type RemoteEvaluator struct{}
func (r *RemoteEvaluator) Weight() int { return 50 } // Higher priority than file evaluator
func (r *RemoteEvaluator) EvaluateFlag(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request) (bool, error) {
    // Custom logic here
    return true, nil
}
func (r *RemoteEvaluator) EvaluateFlagWithDefault(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request, defaultValue bool) bool {
    enabled, err := r.EvaluateFlag(ctx, flagID, tenantID, req)
    if err != nil { return defaultValue }
    return enabled
}

// Register with any service name (name doesn't matter for discovery)
app.RegisterService("remoteEvaluator", &RemoteEvaluator{})
// or  
app.RegisterService("my-custom-flags", &RemoteEvaluator{})

The aggregator automatically discovers all services implementing FeatureFlagEvaluator interface regardless of their registered name. If multiple evaluators have the same name, unique names are automatically generated. Evaluators are called in priority order (lower weight = higher priority), with the built-in file evaluator (weight: 1000) serving as the final fallback.

Migration Note: External evaluators are now discovered by interface matching rather than naming patterns. You can use any service name when registering. See the Feature Flag Migration Guide for detailed migration instructions.

The evaluator interface supports integration with external feature flag services like LaunchDarkly, Split.io, or custom implementations.

Dry Run Mode

Dry run mode enables you to compare responses between different backends, which is particularly useful for testing new services, validating migrations, or A/B testing. When dry run is enabled for a route, requests are sent to both the primary and comparison backends, but only one response is returned to the client while differences are logged for analysis.

Basic Dry Run Configuration
reverseproxy:
  backend_services:
    legacy: "http://legacy.service.com"
    v2: "http://new.service.com"
  
  routes:
    "/api/users": "v2"  # Primary route goes to v2
  
  route_configs:
    "/api/users":
      feature_flag_id: "v2-users-api"
      alternative_backend: "legacy"
      dry_run: true
      dry_run_backend: "v2"  # Backend to compare against
  
  dry_run:
    enabled: true
    log_responses: true
    max_response_size: 1048576  # 1MB
Dry Run with Feature Flags

The most powerful use case combines dry run with feature flags:

feature_flags:
  enabled: true
  flags:
    v2-users-api: false  # Feature flag disabled

route_configs:
  "/api/users":
    feature_flag_id: "v2-users-api"
    alternative_backend: "legacy"
    dry_run: true
    dry_run_backend: "v2"

Behavior when feature flag is disabled:

  • Returns response from alternative_backend (legacy)
  • Compares with dry_run_backend (v2) in background
  • Logs differences for analysis

Behavior when feature flag is enabled:

  • Returns response from primary backend (v2)
  • Compares with dry_run_backend or alternative_backend
  • Logs differences for analysis
Dry Run Configuration Options
dry_run:
  enabled: true                          # Enable dry run globally
  log_responses: true                    # Log response bodies (can be verbose)
  max_response_size: 1048576            # Maximum response size to compare
  compare_headers: ["Content-Type"]      # Specific headers to compare
  ignore_headers: ["Date", "X-Request-ID"]  # Headers to ignore in comparison
  default_response_backend: "primary"   # Which response to return ("primary" or "secondary")
Use Cases
  1. Service Migration: Test new service implementations while serving traffic from stable backend
  2. A/B Testing: Compare different service versions with real traffic
  3. Validation: Ensure new services produce equivalent responses to legacy systems
  4. Performance Testing: Compare response times between different backends
  5. Gradual Rollout: Safely test new features while maintaining fallback options
Monitoring Dry Run Results

Dry run comparisons are logged with detailed information:

{
  "operation": "dry-run",
  "endpoint": "/api/users", 
  "primaryBackend": "legacy",
  "secondaryBackend": "v2",
  "statusCodeMatch": true,
  "headersMatch": false,
  "bodyMatch": false,
  "differences": ["Response body content differs"],
  "primaryResponseTime": "45ms",
  "secondaryResponseTime": "32ms"
}

Use these logs to identify discrepancies and validate that your new services work correctly before fully switching over.

Health Check Configuration

The reverseproxy module provides comprehensive health checking capabilities:

health_check:
  enabled: true                    # Enable health checking
  interval: "30s"                  # Global check interval
  timeout: "5s"                    # Global check timeout
  recent_request_threshold: "60s"  # Skip checks if recent request within threshold
  expected_status_codes: [200, 204] # Global expected status codes
  
  # Custom health endpoints per backend
  health_endpoints:
    api: "/health"
    auth: "/api/health"
  
  # Per-backend health check configuration
  backend_health_check_config:
    api:
      enabled: true
      interval: "15s"              # Override global interval
      timeout: "3s"                # Override global timeout
      expected_status_codes: [200] # Override global status codes
    auth:
      enabled: true
      endpoint: "/status"          # Custom health endpoint
      interval: "45s"
      timeout: "10s"
      expected_status_codes: [200, 201]

Health Check Features:

  • DNS Resolution: Verifies that backend hostnames resolve to IP addresses
  • HTTP Connectivity: Tests HTTP connectivity to backends with configurable timeouts
  • Custom Endpoints: Supports custom health check endpoints per backend
  • Smart Scheduling: Skips health checks if recent requests have occurred
  • Per-Backend Configuration: Allows fine-grained control over health check behavior
  • Status Monitoring: Tracks health status, response times, and error details
  • Metrics Integration: Exposes health status through metrics endpoints
  1. Per-Backend Configuration: Configure path rewriting and header rewriting for each backend service
  2. Per-Endpoint Configuration: Override backend configuration for specific endpoints
  3. Hostname Handling: Control how the Host header is handled for each backend
  4. Header Rewriting: Add, modify, or remove headers before forwarding requests
  5. Path Rewriting: Transform request paths before forwarding to backends
  6. Custom Response Transformers: Create custom functions to transform responses from multiple backends
  7. Custom Endpoint Mappings: Define detailed mappings between frontend endpoints and backend services
  8. Tenant-Specific Routing: Route requests to different backend URLs based on tenant ID

For detailed documentation and examples, see:

License

MIT License

Documentation

Overview

Package reverseproxy provides circuit breaker implementation for backend proxies.

Package reverseproxy provides a flexible reverse proxy module with support for multiple backends, composite responses, and tenant awareness.

Package reverseproxy provides configuration structures for the reverse proxy module.

Package reverseproxy provides error definitions for the reverse proxy module.

Package reverseproxy provides metrics collection for the reverse proxy module.

Package reverseproxy provides a flexible reverse proxy module with support for multiple backends, composite responses, and tenant awareness.

Package reverseproxy provides retry functionality for the reverse proxy module.

Package reverseproxy provides a flexible reverse proxy module with support for multiple backends, composite responses, and tenant awareness.

Index

Constants

View Source
const (
	// Configuration events
	EventTypeConfigLoaded    = "com.modular.reverseproxy.config.loaded"
	EventTypeConfigValidated = "com.modular.reverseproxy.config.validated"

	// Proxy events
	EventTypeProxyCreated = "com.modular.reverseproxy.proxy.created"
	EventTypeProxyStarted = "com.modular.reverseproxy.proxy.started"
	EventTypeProxyStopped = "com.modular.reverseproxy.proxy.stopped"

	// Request events
	EventTypeRequestReceived = "com.modular.reverseproxy.request.received"
	EventTypeRequestProxied  = "com.modular.reverseproxy.request.proxied"
	EventTypeRequestFailed   = "com.modular.reverseproxy.request.failed"

	// Backend events
	EventTypeBackendHealthy   = "com.modular.reverseproxy.backend.healthy"
	EventTypeBackendUnhealthy = "com.modular.reverseproxy.backend.unhealthy"
	EventTypeBackendAdded     = "com.modular.reverseproxy.backend.added"
	EventTypeBackendRemoved   = "com.modular.reverseproxy.backend.removed"

	// Load balancing events
	EventTypeLoadBalanceDecision   = "com.modular.reverseproxy.loadbalance.decision"
	EventTypeLoadBalanceRoundRobin = "com.modular.reverseproxy.loadbalance.roundrobin"

	// Circuit breaker events
	EventTypeCircuitBreakerOpen     = "com.modular.reverseproxy.circuitbreaker.open"
	EventTypeCircuitBreakerClosed   = "com.modular.reverseproxy.circuitbreaker.closed"
	EventTypeCircuitBreakerHalfOpen = "com.modular.reverseproxy.circuitbreaker.halfopen"

	// Module lifecycle events
	EventTypeModuleStarted = "com.modular.reverseproxy.module.started"
	EventTypeModuleStopped = "com.modular.reverseproxy.module.stopped"

	// Error events
	EventTypeError = "com.modular.reverseproxy.error"
)

Event type constants for reverseproxy module events. Following CloudEvents specification reverse domain notation.

Variables

View Source
var (
	// ErrCircuitOpen defined in circuit_breaker.go
	ErrMaxRetriesReached        = errors.New("maximum number of retries reached")
	ErrRequestTimeout           = errors.New("request timed out")
	ErrNoAvailableBackend       = errors.New("no available backend")
	ErrBackendServiceNotFound   = errors.New("backend service not found")
	ErrConfigurationNil         = errors.New("configuration is nil")
	ErrDefaultBackendNotDefined = errors.New("default backend is not defined in backend_services")
	ErrTenantIDRequired         = errors.New("tenant ID is required but TenantIDHeader is not set")
	ErrServiceNotHandleFunc     = errors.New("service does not implement HandleFunc interface")
	ErrCannotRegisterRoutes     = errors.New("cannot register routes: router is nil")
	ErrBackendNotFound          = errors.New("backend not found")
	ErrBackendProxyNil          = errors.New("backend proxy is nil")
	ErrFeatureFlagNotFound      = errors.New("feature flag not found")
	ErrDryRunModeNotEnabled     = errors.New("dry-run mode is not enabled")
	ErrApplicationNil           = errors.New("app cannot be nil")
	ErrLoggerNil                = errors.New("logger cannot be nil")

	// Feature flag evaluation sentinel errors
	ErrNoDecision     = errors.New("no-decision")     // Evaluator abstains from making a decision
	ErrEvaluatorFatal = errors.New("evaluator-fatal") // Fatal error that should abort evaluation chain

	// Feature flag aggregator errors
	ErrNoEvaluatorsAvailable = errors.New("no feature flag evaluators available")
	ErrNoEvaluatorDecision   = errors.New("no evaluator provided decision for flag")

	// Event observation errors
	ErrNoSubjectForEventEmission = errors.New("no subject available for event emission")
)

Error definitions for the reverse proxy module.

View Source
var (
	// ErrCircuitOpen is returned when the circuit is open and requests are not allowed.
	ErrCircuitOpen = errors.New("circuit breaker is open")
)
View Source
var ErrNoHostname = errors.New("no hostname in URL")

ErrNoHostname is returned when a URL has no hostname

View Source
var ErrUnexpectedConfigType = errors.New("unexpected config type")

ErrUnexpectedConfigType is returned when an unexpected config type is passed to Init

View Source
var ErrUnexpectedStatusCode = errors.New("unexpected status code")

ErrUnexpectedStatusCode is returned when a health check receives an unexpected status code

Functions

func ProvideConfig

func ProvideConfig() interface{}

ProvideConfig creates a new default configuration for the reverseproxy module. This is used by the modular framework to register the configuration.

func RetryWithPolicy

func RetryWithPolicy(ctx context.Context, policy RetryPolicy, fn RetryFunc, metrics *MetricsCollector, backendID string) (interface{}, int, error)

RetryWithPolicy executes the given function with retries according to the policy.

func TenantIDFromRequest

func TenantIDFromRequest(tenantHeader string, r *http.Request) (string, bool)

TenantIDFromRequest extracts tenant ID from the request header

Types

type Backend

type Backend struct {
	ID     string
	URL    string
	Client *http.Client
}

Backend represents a backend service configuration.

type BackendConfig

type BackendConfig struct {
	URL                 string                `json:"url" yaml:"url"`
	Timeout             time.Duration         `json:"timeout" yaml:"timeout"`
	MaxIdleConns        int                   `json:"max_idle_conns" yaml:"max_idle_conns"`
	MaxIdleConnsPerHost int                   `json:"max_idle_conns_per_host" yaml:"max_idle_conns_per_host"`
	MaxConnsPerHost     int                   `json:"max_conns_per_host" yaml:"max_conns_per_host"`
	IdleConnTimeout     time.Duration         `json:"idle_conn_timeout" yaml:"idle_conn_timeout"`
	TLSSkipVerify       bool                  `json:"tls_skip_verify" yaml:"tls_skip_verify"`
	CircuitBreaker      *CircuitBreakerConfig `json:"circuit_breaker" yaml:"circuit_breaker"`
	Retry               *RetryConfig          `json:"retry" yaml:"retry"`
}

BackendConfig provides configuration for a backend server.

type BackendEndpointRequest

type BackendEndpointRequest struct {
	Backend     string
	Method      string
	Path        string
	Headers     map[string]string
	QueryParams map[string]string
}

BackendEndpointRequest defines a request to be sent to a backend

type BackendHealthConfig added in v1.1.2

type BackendHealthConfig struct {
	Enabled             bool          `json:"enabled" yaml:"enabled" toml:"enabled" env:"ENABLED" default:"true" desc:"Enable health checking for this backend"`
	Endpoint            string        `json:"endpoint" yaml:"endpoint" toml:"endpoint" env:"ENDPOINT" desc:"Custom health check endpoint (defaults to base URL)"`
	Interval            time.Duration `json:"interval" yaml:"interval" toml:"interval" env:"INTERVAL" desc:"Override global interval for this backend"`
	Timeout             time.Duration `json:"timeout" yaml:"timeout" toml:"timeout" env:"TIMEOUT" desc:"Override global timeout for this backend"`
	ExpectedStatusCodes []int         `` /* 176-byte string literal not displayed */
}

BackendHealthConfig provides per-backend health check configuration.

type BackendServiceConfig added in v1.1.2

type BackendServiceConfig struct {
	// URL is the base URL for the backend service
	URL string `json:"url" yaml:"url" toml:"url" env:"URL"`

	// PathRewriting defines path rewriting rules specific to this backend
	PathRewriting PathRewritingConfig `json:"path_rewriting" yaml:"path_rewriting" toml:"path_rewriting"`

	// HeaderRewriting defines header rewriting rules specific to this backend
	HeaderRewriting HeaderRewritingConfig `json:"header_rewriting" yaml:"header_rewriting" toml:"header_rewriting"`

	// Endpoints defines endpoint-specific configurations
	Endpoints map[string]EndpointConfig `json:"endpoints" yaml:"endpoints" toml:"endpoints"`

	// FeatureFlagID is the ID of the feature flag that controls whether this backend is enabled
	// If specified and the feature flag evaluates to false, requests to this backend will fail or use alternative
	FeatureFlagID string `json:"feature_flag_id" yaml:"feature_flag_id" toml:"feature_flag_id" env:"FEATURE_FLAG_ID"`

	// AlternativeBackend specifies an alternative backend to use when the feature flag is disabled
	// If FeatureFlagID is specified and evaluates to false, requests will be routed to this backend instead
	AlternativeBackend string `json:"alternative_backend" yaml:"alternative_backend" toml:"alternative_backend" env:"ALTERNATIVE_BACKEND"`
}

BackendServiceConfig defines configuration for a specific backend service.

type CachedResponse

type CachedResponse struct {
	StatusCode     int
	Headers        http.Header
	Body           []byte
	LastAccessed   time.Time
	ExpirationTime time.Time
}

CachedResponse represents a cached HTTP response

type CircuitBreaker

type CircuitBreaker struct {
	// contains filtered or unexported fields
}

CircuitBreaker implements the circuit breaker pattern for HTTP requests.

func NewCircuitBreaker

func NewCircuitBreaker(backendName string, metricsCollector *MetricsCollector) *CircuitBreaker

NewCircuitBreaker creates a new CircuitBreaker with default settings.

func NewCircuitBreakerWithConfig

func NewCircuitBreakerWithConfig(backendName string, config CircuitBreakerConfig, metricsCollector *MetricsCollector) *CircuitBreaker

NewCircuitBreakerWithConfig creates a new CircuitBreaker with custom settings.

func (*CircuitBreaker) Execute

func (cb *CircuitBreaker) Execute(req *http.Request, fn func(*http.Request) (*http.Response, error)) (*http.Response, error)

Execute executes the provided function with circuit breaker protection.

func (*CircuitBreaker) GetFailureCount added in v1.2.0

func (cb *CircuitBreaker) GetFailureCount() int

GetFailureCount returns the current failure count of the circuit breaker.

func (*CircuitBreaker) GetState

func (cb *CircuitBreaker) GetState() CircuitState

GetState returns the current state of the circuit breaker.

func (*CircuitBreaker) IsOpen

func (cb *CircuitBreaker) IsOpen() bool

IsOpen returns true if the circuit is open (no requests should be made).

func (*CircuitBreaker) RecordFailure

func (cb *CircuitBreaker) RecordFailure()

RecordFailure records a failed request and opens the circuit if threshold is reached.

func (*CircuitBreaker) RecordSuccess

func (cb *CircuitBreaker) RecordSuccess()

RecordSuccess records a successful request and resets the failure count.

func (*CircuitBreaker) Reset

func (cb *CircuitBreaker) Reset()

Reset resets the circuit breaker to closed state (capitalized for backward compatibility).

func (*CircuitBreaker) WithFailureThreshold

func (cb *CircuitBreaker) WithFailureThreshold(threshold int) *CircuitBreaker

WithFailureThreshold sets the number of failures required to open the circuit.

func (*CircuitBreaker) WithMetricsCollector

func (cb *CircuitBreaker) WithMetricsCollector(collector *MetricsCollector) *CircuitBreaker

WithMetricsCollector associates a metrics collector with this circuit breaker.

func (*CircuitBreaker) WithRequestTimeout

func (cb *CircuitBreaker) WithRequestTimeout(timeout time.Duration) *CircuitBreaker

WithRequestTimeout sets the timeout for each request protected by this circuit breaker.

func (*CircuitBreaker) WithResetTimeout

func (cb *CircuitBreaker) WithResetTimeout(timeout time.Duration) *CircuitBreaker

WithResetTimeout sets the duration to wait before allowing a test request.

type CircuitBreakerConfig

type CircuitBreakerConfig struct {
	Enabled                 bool          `json:"enabled" yaml:"enabled" toml:"enabled" env:"ENABLED"`
	FailureThreshold        int           `json:"failure_threshold" yaml:"failure_threshold" toml:"failure_threshold" env:"FAILURE_THRESHOLD"`
	SuccessThreshold        int           `json:"success_threshold" yaml:"success_threshold" toml:"success_threshold" env:"SUCCESS_THRESHOLD"`
	OpenTimeout             time.Duration `json:"open_timeout" yaml:"open_timeout" toml:"open_timeout" env:"OPEN_TIMEOUT"`
	HalfOpenAllowedRequests int           `` /* 134-byte string literal not displayed */
	WindowSize              int           `json:"window_size" yaml:"window_size" toml:"window_size" env:"WINDOW_SIZE"`
	SuccessRateThreshold    float64       `json:"success_rate_threshold" yaml:"success_rate_threshold" toml:"success_rate_threshold" env:"SUCCESS_RATE_THRESHOLD"`
}

CircuitBreakerConfig provides configuration for the circuit breaker.

type CircuitBreakerInfo added in v1.1.5

type CircuitBreakerInfo struct {
	State        string    `json:"state"`
	FailureCount int       `json:"failureCount"`
	Failures     int       `json:"failures"` // alias field expected by tests
	SuccessCount int       `json:"successCount"`
	LastFailure  time.Time `json:"lastFailure,omitempty"`
	LastAttempt  time.Time `json:"lastAttempt,omitempty"`
}

CircuitBreakerInfo represents circuit breaker status information.

type CircuitBreakerProvider added in v1.2.0

type CircuitBreakerProvider func(backendID string) *HealthCircuitBreakerInfo

CircuitBreakerProvider defines a function to get circuit breaker information for a backend.

type CircuitState

type CircuitState int

CircuitState represents the state of a circuit breaker.

const (
	// StateClosed indicates the circuit is closed and allowing requests.
	StateClosed CircuitState = iota
	// StateOpen indicates the circuit is open and blocking requests.
	StateOpen
	// StateHalfOpen indicates the circuit is allowing a test request.
	StateHalfOpen
)

func (CircuitState) String

func (s CircuitState) String() string

String returns a string representation of the circuit state.

type ComparisonResult added in v1.1.5

type ComparisonResult struct {
	StatusCodeMatch bool                  `json:"statusCodeMatch"`
	HeadersMatch    bool                  `json:"headersMatch"`
	BodyMatch       bool                  `json:"bodyMatch"`
	Differences     []string              `json:"differences,omitempty"`
	HeaderDiffs     map[string]HeaderDiff `json:"headerDiffs,omitempty"`
}

ComparisonResult contains the results of comparing two responses.

type CompositeHandler

type CompositeHandler struct {
	// contains filtered or unexported fields
}

CompositeHandler is updated to handle multiple requests and process/merge them into a single response. It now includes circuit breaking and response caching.

func NewCompositeHandler

func NewCompositeHandler(backends []*Backend, parallel bool, responseTimeout time.Duration) *CompositeHandler

NewCompositeHandler creates a new composite handler with the given backends.

func (*CompositeHandler) ConfigureCircuitBreakers

func (h *CompositeHandler) ConfigureCircuitBreakers(globalConfig CircuitBreakerConfig, backendConfigs map[string]CircuitBreakerConfig)

ConfigureCircuitBreakers sets up circuit breakers for each backend using the provided configuration

func (*CompositeHandler) ServeHTTP

func (h *CompositeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP handles the request by forwarding it to all backends and merging the responses.

func (*CompositeHandler) SetResponseCache

func (h *CompositeHandler) SetResponseCache(cache *responseCache)

SetResponseCache sets a response cache for the handler.

type CompositeResponse

type CompositeResponse struct {
	StatusCode int
	Headers    http.Header
	Body       []byte
}

CompositeResponse represents a transformed response from multiple backend requests

type CompositeRoute

type CompositeRoute struct {
	Pattern  string   `json:"pattern" yaml:"pattern" toml:"pattern" env:"PATTERN"`
	Backends []string `json:"backends" yaml:"backends" toml:"backends" env:"BACKENDS"`
	Strategy string   `json:"strategy" yaml:"strategy" toml:"strategy" env:"STRATEGY"`

	// FeatureFlagID is the ID of the feature flag that controls whether this composite route is enabled
	// If specified and the feature flag evaluates to false, this route will return 404
	FeatureFlagID string `json:"feature_flag_id" yaml:"feature_flag_id" toml:"feature_flag_id" env:"FEATURE_FLAG_ID"`

	// AlternativeBackend specifies an alternative single backend to use when the feature flag is disabled
	// If FeatureFlagID is specified and evaluates to false, requests will be routed to this backend instead
	AlternativeBackend string `json:"alternative_backend" yaml:"alternative_backend" toml:"alternative_backend" env:"ALTERNATIVE_BACKEND"`
}

CompositeRoute defines a route that combines responses from multiple backends.

type Config

type Config struct {
	Backends       map[string]BackendConfig `json:"backends" yaml:"backends"`
	PrefixMapping  map[string]string        `json:"prefix_mapping" yaml:"prefix_mapping"`
	ExactMapping   map[string]string        `json:"exact_mapping" yaml:"exact_mapping"`
	MetricsEnabled bool                     `json:"metrics_enabled" yaml:"metrics_enabled"`
	MetricsPath    string                   `json:"metrics_path" yaml:"metrics_path"`
	CircuitBreaker CircuitBreakerConfig     `json:"circuit_breaker" yaml:"circuit_breaker"`
	Retry          RetryConfig              `json:"retry" yaml:"retry"`
}

Config provides configuration options for the ReverseProxyModule. This is the original Config struct which is being phased out in favor of ReverseProxyConfig.

type DebugEndpointsConfig added in v1.1.5

type DebugEndpointsConfig struct {
	// Enabled determines if debug endpoints should be available
	Enabled bool `json:"enabled" yaml:"enabled" toml:"enabled" env:"DEBUG_ENDPOINTS_ENABLED" default:"false"`

	// BasePath is the base path for debug endpoints
	BasePath string `json:"base_path" yaml:"base_path" toml:"base_path" env:"DEBUG_BASE_PATH" default:"/debug"`

	// RequireAuth determines if debug endpoints require authentication
	RequireAuth bool `json:"require_auth" yaml:"require_auth" toml:"require_auth" env:"DEBUG_REQUIRE_AUTH" default:"false"`

	// AuthToken is the token required for debug endpoint access (if RequireAuth is true)
	AuthToken string `json:"auth_token" yaml:"auth_token" toml:"auth_token" env:"DEBUG_AUTH_TOKEN"`
}

DebugEndpointsConfig provides configuration for debug endpoints.

type DebugHandler added in v1.1.5

type DebugHandler struct {
	// contains filtered or unexported fields
}

DebugHandler handles debug endpoint requests.

func NewDebugHandler added in v1.1.5

func NewDebugHandler(config DebugEndpointsConfig, featureFlagEval FeatureFlagEvaluator, proxyConfig *ReverseProxyConfig, tenantService modular.TenantService, logger modular.Logger) *DebugHandler

NewDebugHandler creates a new debug handler.

func (*DebugHandler) HandleBackends added in v1.2.1

func (d *DebugHandler) HandleBackends(w http.ResponseWriter, r *http.Request)

HandleBackends handles the backends debug endpoint.

func (*DebugHandler) HandleCircuitBreakers added in v1.2.1

func (d *DebugHandler) HandleCircuitBreakers(w http.ResponseWriter, r *http.Request)

HandleCircuitBreakers handles the circuit breakers debug endpoint.

func (*DebugHandler) HandleFlags added in v1.2.1

func (d *DebugHandler) HandleFlags(w http.ResponseWriter, r *http.Request)

HandleFlags handles the feature flags debug endpoint.

func (*DebugHandler) HandleHealthChecks added in v1.2.1

func (d *DebugHandler) HandleHealthChecks(w http.ResponseWriter, r *http.Request)

HandleHealthChecks handles the health checks debug endpoint.

func (*DebugHandler) HandleInfo added in v1.2.1

func (d *DebugHandler) HandleInfo(w http.ResponseWriter, r *http.Request)

HandleInfo handles the general debug info endpoint.

func (*DebugHandler) RegisterRoutes added in v1.1.5

func (d *DebugHandler) RegisterRoutes(mux *http.ServeMux)

RegisterRoutes registers debug endpoint routes with the provided mux.

func (*DebugHandler) SetCircuitBreakers added in v1.1.5

func (d *DebugHandler) SetCircuitBreakers(circuitBreakers map[string]*CircuitBreaker)

SetCircuitBreakers updates the circuit breakers reference for debugging.

func (*DebugHandler) SetHealthCheckers added in v1.1.5

func (d *DebugHandler) SetHealthCheckers(healthCheckers map[string]*HealthChecker)

SetHealthCheckers updates the health checkers reference for debugging.

type DebugInfo added in v1.1.5

type DebugInfo struct {
	Timestamp       time.Time                     `json:"timestamp"`
	Tenant          string                        `json:"tenant,omitempty"`
	Environment     string                        `json:"environment"`
	Flags           map[string]interface{}        `json:"flags,omitempty"`
	BackendServices map[string]string             `json:"backendServices"`
	Routes          map[string]string             `json:"routes"`
	CircuitBreakers map[string]CircuitBreakerInfo `json:"circuitBreakers,omitempty"`
	HealthChecks    map[string]HealthInfo         `json:"healthChecks,omitempty"`
}

DebugInfo represents debugging information about the reverse proxy state.

type DryRunConfig added in v1.1.5

type DryRunConfig struct {
	// Enabled determines if dry-run mode is available
	Enabled bool `json:"enabled" yaml:"enabled" toml:"enabled" env:"DRY_RUN_ENABLED" default:"false"`

	// LogResponses determines if response bodies should be logged (can be verbose)
	LogResponses bool `json:"log_responses" yaml:"log_responses" toml:"log_responses" env:"DRY_RUN_LOG_RESPONSES" default:"false"`

	// MaxResponseSize is the maximum response size to compare (in bytes)
	MaxResponseSize int64 `json:"max_response_size" yaml:"max_response_size" toml:"max_response_size" env:"DRY_RUN_MAX_RESPONSE_SIZE" default:"1048576"` // 1MB

	// CompareHeaders determines which headers should be compared
	CompareHeaders []string `json:"compare_headers" yaml:"compare_headers" toml:"compare_headers" env:"DRY_RUN_COMPARE_HEADERS"`

	// IgnoreHeaders lists headers to ignore during comparison
	IgnoreHeaders []string `json:"ignore_headers" yaml:"ignore_headers" toml:"ignore_headers" env:"DRY_RUN_IGNORE_HEADERS"`

	// DefaultResponseBackend specifies which backend response to return by default ("primary" or "secondary")
	DefaultResponseBackend string `` /* 152-byte string literal not displayed */
}

DryRunConfig provides configuration for dry-run functionality.

type DryRunHandler added in v1.1.5

type DryRunHandler struct {
	// contains filtered or unexported fields
}

DryRunHandler handles dry-run request processing.

func NewDryRunHandler added in v1.1.5

func NewDryRunHandler(config DryRunConfig, tenantIDHeader string, logger modular.Logger) *DryRunHandler

NewDryRunHandler creates a new dry-run handler.

func (*DryRunHandler) ProcessDryRun added in v1.1.5

func (d *DryRunHandler) ProcessDryRun(ctx context.Context, req *http.Request, primaryBackend, secondaryBackend string) (*DryRunResult, error)

ProcessDryRun processes a request in dry-run mode, sending it to both backends and comparing responses.

type DryRunResult added in v1.1.5

type DryRunResult struct {
	Timestamp         time.Time        `json:"timestamp"`
	RequestID         string           `json:"requestId,omitempty"`
	TenantID          string           `json:"tenantId,omitempty"`
	Endpoint          string           `json:"endpoint"`
	Method            string           `json:"method"`
	PrimaryBackend    string           `json:"primaryBackend"`
	SecondaryBackend  string           `json:"secondaryBackend"`
	PrimaryResponse   ResponseInfo     `json:"primaryResponse"`
	SecondaryResponse ResponseInfo     `json:"secondaryResponse"`
	Comparison        ComparisonResult `json:"comparison"`
	Duration          DurationInfo     `json:"duration"`
	ReturnedResponse  string           `json:"returnedResponse"` // "primary" or "secondary" - indicates which response was returned to client
}

DryRunResult represents the result of a dry-run comparison.

func (*DryRunResult) GetReturnedResponse added in v1.1.5

func (d *DryRunResult) GetReturnedResponse() ResponseInfo

GetReturnedResponse returns the response information that should be sent to the client.

type DurationInfo added in v1.1.5

type DurationInfo struct {
	Total     time.Duration `json:"total"`
	Primary   time.Duration `json:"primary"`
	Secondary time.Duration `json:"secondary"`
}

DurationInfo contains timing information for the dry-run.

type EndpointConfig added in v1.1.2

type EndpointConfig struct {
	// Pattern is the URL pattern that this endpoint matches (e.g., "/api/v1/users/*")
	Pattern string `json:"pattern" yaml:"pattern" toml:"pattern" env:"PATTERN"`

	// PathRewriting defines path rewriting rules specific to this endpoint
	PathRewriting PathRewritingConfig `json:"path_rewriting" yaml:"path_rewriting" toml:"path_rewriting"`

	// HeaderRewriting defines header rewriting rules specific to this endpoint
	HeaderRewriting HeaderRewritingConfig `json:"header_rewriting" yaml:"header_rewriting" toml:"header_rewriting"`

	// FeatureFlagID is the ID of the feature flag that controls whether this endpoint is enabled
	// If specified and the feature flag evaluates to false, this endpoint will be skipped
	FeatureFlagID string `json:"feature_flag_id" yaml:"feature_flag_id" toml:"feature_flag_id" env:"FEATURE_FLAG_ID"`

	// AlternativeBackend specifies an alternative backend to use when the feature flag is disabled
	// If FeatureFlagID is specified and evaluates to false, requests will be routed to this backend instead
	AlternativeBackend string `json:"alternative_backend" yaml:"alternative_backend" toml:"alternative_backend" env:"ALTERNATIVE_BACKEND"`
}

EndpointConfig defines configuration for a specific endpoint within a backend service.

type EndpointMapping

type EndpointMapping struct {
	// Endpoints lists the backend requests to make
	Endpoints []BackendEndpointRequest

	// ResponseTransformer is a function that transforms multiple backend responses
	// into a single composite response
	ResponseTransformer func(ctx context.Context, req *http.Request, responses map[string]*http.Response) (*CompositeResponse, error)
}

EndpointMapping defines how requests should be routed to different backends and how their responses should be combined

type EndpointRewriteRule added in v1.1.2

type EndpointRewriteRule struct {
	// Pattern is the incoming request pattern to match (e.g., "/api/v1/users")
	Pattern string `json:"pattern" yaml:"pattern" toml:"pattern" env:"PATTERN"`

	// Replacement is the new path to use when forwarding to backend (e.g., "/users")
	Replacement string `json:"replacement" yaml:"replacement" toml:"replacement" env:"REPLACEMENT"`

	// Backend specifies which backend this rule applies to (optional, applies to all if empty)
	Backend string `json:"backend" yaml:"backend" toml:"backend" env:"BACKEND"`

	// StripQueryParams removes query parameters from the request when forwarding
	StripQueryParams bool `json:"strip_query_params" yaml:"strip_query_params" toml:"strip_query_params" env:"STRIP_QUERY_PARAMS"`
}

EndpointRewriteRule defines a rewrite rule for a specific endpoint pattern.

type FeatureFlagAggregator added in v1.3.3

type FeatureFlagAggregator struct {
	// contains filtered or unexported fields
}

FeatureFlagAggregator implements FeatureFlagEvaluator by aggregating multiple evaluators and calling them in priority order (weight-based). It discovers evaluators from the service registry by name prefix pattern.

func NewFeatureFlagAggregator added in v1.3.3

func NewFeatureFlagAggregator(app modular.Application, logger *slog.Logger) *FeatureFlagAggregator

NewFeatureFlagAggregator creates a new aggregator that discovers and coordinates multiple feature flag evaluators from the application's service registry.

func (*FeatureFlagAggregator) EvaluateFlag added in v1.3.3

func (a *FeatureFlagAggregator) EvaluateFlag(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request) (bool, error)

EvaluateFlag implements FeatureFlagEvaluator by calling discovered evaluators in weight order until one returns a decision or all have been tried.

func (*FeatureFlagAggregator) EvaluateFlagWithDefault added in v1.3.3

func (a *FeatureFlagAggregator) EvaluateFlagWithDefault(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request, defaultValue bool) bool

EvaluateFlagWithDefault implements FeatureFlagEvaluator by calling EvaluateFlag and returning the default value if evaluation fails.

type FeatureFlagEvaluator added in v1.1.3

type FeatureFlagEvaluator interface {
	// EvaluateFlag evaluates a feature flag for the given context and request.
	// Returns true if the feature flag is enabled, false otherwise.
	// The tenantID parameter can be empty if no tenant context is available.
	//
	// Special error handling:
	// - Returning ErrNoDecision allows evaluation to continue to next evaluator
	// - Returning ErrEvaluatorFatal stops evaluation chain immediately
	// - Other errors are treated as non-fatal and evaluation continues
	EvaluateFlag(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request) (bool, error)

	// EvaluateFlagWithDefault evaluates a feature flag with a default value.
	// If evaluation fails or the flag doesn't exist, returns the default value.
	EvaluateFlagWithDefault(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request, defaultValue bool) bool
}

FeatureFlagEvaluator defines the interface for evaluating feature flags. This allows for different implementations of feature flag services while providing a consistent interface for the reverseproxy module.

Evaluators may return special sentinel errors to control aggregation behavior:

  • ErrNoDecision: Evaluator abstains and evaluation continues to next evaluator
  • ErrEvaluatorFatal: Fatal error that stops evaluation chain immediately

type FeatureFlagsConfig added in v1.1.5

type FeatureFlagsConfig struct {
	// Enabled determines whether to create and expose the built-in FileBasedFeatureFlagEvaluator service
	Enabled bool `` /* 143-byte string literal not displayed */

	// Flags defines default values for feature flags. Tenant-specific overrides come from tenant config files.
	Flags map[string]bool `json:"flags" yaml:"flags" toml:"flags" desc:"Default values for feature flags"`
}

FeatureFlagsConfig provides configuration for the built-in feature flag evaluator.

type FileBasedFeatureFlagEvaluator added in v1.1.3

type FileBasedFeatureFlagEvaluator struct {
	// contains filtered or unexported fields
}

FileBasedFeatureFlagEvaluator implements a feature flag evaluator that integrates with the Modular framework's tenant-aware configuration system.

func NewFileBasedFeatureFlagEvaluator added in v1.1.3

func NewFileBasedFeatureFlagEvaluator(app modular.Application, logger *slog.Logger) (*FileBasedFeatureFlagEvaluator, error)

NewFileBasedFeatureFlagEvaluator creates a new tenant-aware feature flag evaluator.

func (*FileBasedFeatureFlagEvaluator) EvaluateFlag added in v1.1.3

func (f *FileBasedFeatureFlagEvaluator) EvaluateFlag(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request) (bool, error)

EvaluateFlag evaluates a feature flag using tenant-aware configuration. It follows the standard Modular framework pattern where: 1. Default flags come from the main configuration 2. Tenant-specific overrides come from tenant configuration files 3. During request processing, tenant context determines which configuration to use

func (*FileBasedFeatureFlagEvaluator) EvaluateFlagWithDefault added in v1.1.3

func (f *FileBasedFeatureFlagEvaluator) EvaluateFlagWithDefault(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request, defaultValue bool) bool

EvaluateFlagWithDefault evaluates a feature flag with a default value.

type HeaderDiff added in v1.1.5

type HeaderDiff struct {
	Primary   string `json:"primary"`
	Secondary string `json:"secondary"`
}

HeaderDiff represents a difference in header values.

type HeaderRewritingConfig added in v1.1.2

type HeaderRewritingConfig struct {
	// HostnameHandling controls how the Host header is handled
	HostnameHandling HostnameHandlingMode `json:"hostname_handling" yaml:"hostname_handling" toml:"hostname_handling" env:"HOSTNAME_HANDLING"`

	// CustomHostname sets a custom hostname to use instead of the original or backend hostname
	CustomHostname string `json:"custom_hostname" yaml:"custom_hostname" toml:"custom_hostname" env:"CUSTOM_HOSTNAME"`

	// SetHeaders defines headers to set or override on the request
	SetHeaders map[string]string `json:"set_headers" yaml:"set_headers" toml:"set_headers"`

	// RemoveHeaders defines headers to remove from the request
	RemoveHeaders []string `json:"remove_headers" yaml:"remove_headers" toml:"remove_headers"`
}

HeaderRewritingConfig defines configuration for header rewriting rules.

type HealthCheckConfig added in v1.1.2

type HealthCheckConfig struct {
	Enabled                  bool                           `json:"enabled" yaml:"enabled" toml:"enabled" env:"ENABLED" default:"false" desc:"Enable health checking for backend services"`
	Interval                 time.Duration                  `json:"interval" yaml:"interval" toml:"interval" env:"INTERVAL" default:"30s" desc:"Interval between health checks"`
	Timeout                  time.Duration                  `json:"timeout" yaml:"timeout" toml:"timeout" env:"TIMEOUT" default:"5s" desc:"Timeout for health check requests"`
	RecentRequestThreshold   time.Duration                  `` /* 219-byte string literal not displayed */
	HealthEndpoints          map[string]string              `` /* 176-byte string literal not displayed */
	ExpectedStatusCodes      []int                          `` /* 174-byte string literal not displayed */
	BackendHealthCheckConfig map[string]BackendHealthConfig `` /* 150-byte string literal not displayed */
}

HealthCheckConfig provides configuration for backend health checking.

type HealthChecker added in v1.1.2

type HealthChecker struct {
	// contains filtered or unexported fields
}

HealthChecker manages health checking for backend services.

func NewHealthChecker added in v1.1.2

func NewHealthChecker(config *HealthCheckConfig, backends map[string]string, httpClient *http.Client, logger *slog.Logger) *HealthChecker

NewHealthChecker creates a new health checker with the given configuration.

func (*HealthChecker) GetBackendHealthStatus added in v1.1.2

func (hc *HealthChecker) GetBackendHealthStatus(backendID string) (*HealthStatus, bool)

GetBackendHealthStatus returns the health status for a specific backend.

func (*HealthChecker) GetHealthStatus added in v1.1.2

func (hc *HealthChecker) GetHealthStatus() map[string]*HealthStatus

GetHealthStatus returns the current health status for all backends.

func (*HealthChecker) GetOverallHealthStatus added in v1.2.0

func (hc *HealthChecker) GetOverallHealthStatus(includeDetails bool) *OverallHealthStatus

GetOverallHealthStatus returns the overall health status of all backends. The service is considered healthy if all configured backends are healthy.

func (*HealthChecker) IsRunning added in v1.1.2

func (hc *HealthChecker) IsRunning() bool

IsRunning returns whether the health checker is currently running.

func (*HealthChecker) RecordBackendRequest added in v1.1.2

func (hc *HealthChecker) RecordBackendRequest(backendID string)

RecordBackendRequest records that a request was made to a backend.

func (*HealthChecker) SetCircuitBreakerProvider added in v1.2.0

func (hc *HealthChecker) SetCircuitBreakerProvider(provider CircuitBreakerProvider)

SetCircuitBreakerProvider sets the circuit breaker provider function.

func (*HealthChecker) SetEventEmitter added in v1.3.3

func (hc *HealthChecker) SetEventEmitter(emitter HealthEventEmitter)

SetEventEmitter sets the callback used to emit health events.

func (*HealthChecker) Start added in v1.1.2

func (hc *HealthChecker) Start(ctx context.Context) error

Start begins the health checking process.

func (*HealthChecker) Stop added in v1.1.2

func (hc *HealthChecker) Stop(ctx context.Context)

Stop stops the health checking process.

func (*HealthChecker) UpdateBackends added in v1.1.2

func (hc *HealthChecker) UpdateBackends(ctx context.Context, backends map[string]string)

UpdateBackends updates the list of backends to monitor.

func (*HealthChecker) UpdateHealthConfig added in v1.3.3

func (hc *HealthChecker) UpdateHealthConfig(ctx context.Context, cfg *HealthCheckConfig)

UpdateHealthConfig replaces internal copies of health-related configuration maps atomically.

type HealthCircuitBreakerInfo added in v1.2.0

type HealthCircuitBreakerInfo struct {
	IsOpen       bool
	State        string
	FailureCount int
}

HealthCircuitBreakerInfo provides circuit breaker status information for health checks.

type HealthEventEmitter added in v1.3.3

type HealthEventEmitter func(eventType string, data map[string]interface{})

HealthEventEmitter is a callback used to emit backend health events. Accepts event type and a data map for the event payload.

type HealthInfo added in v1.1.5

type HealthInfo struct {
	Status       string    `json:"status"`
	LastCheck    time.Time `json:"lastCheck,omitempty"`
	ResponseTime string    `json:"responseTime,omitempty"`
	StatusCode   int       `json:"statusCode,omitempty"`
}

HealthInfo represents backend health information.

type HealthStatus added in v1.1.2

type HealthStatus struct {
	BackendID        string        `json:"backend_id"`
	URL              string        `json:"url"`
	Healthy          bool          `json:"healthy"`
	LastCheck        time.Time     `json:"last_check"`
	LastSuccess      time.Time     `json:"last_success"`
	LastError        string        `json:"last_error,omitempty"`
	ResponseTime     time.Duration `json:"response_time"`
	DNSResolved      bool          `json:"dns_resolved"`
	ResolvedIPs      []string      `json:"resolved_ips,omitempty"`
	LastRequest      time.Time     `json:"last_request"`
	ChecksSkipped    int64         `json:"checks_skipped"`
	TotalChecks      int64         `json:"total_checks"`
	SuccessfulChecks int64         `json:"successful_checks"`
	// Circuit breaker status
	CircuitBreakerOpen  bool   `json:"circuit_breaker_open"`
	CircuitBreakerState string `json:"circuit_breaker_state,omitempty"`
	CircuitFailureCount int    `json:"circuit_failure_count,omitempty"`
	// Health check result (independent of circuit breaker status)
	HealthCheckPassing bool `json:"health_check_passing"`
}

HealthStatus represents the health status of a backend service.

type HostnameHandlingMode added in v1.1.2

type HostnameHandlingMode string

HostnameHandlingMode defines how the Host header should be handled when forwarding requests.

const (
	// HostnamePreserveOriginal preserves the original client's Host header (default)
	HostnamePreserveOriginal HostnameHandlingMode = "preserve_original"

	// HostnameUseBackend uses the backend service's hostname
	HostnameUseBackend HostnameHandlingMode = "use_backend"

	// HostnameUseCustom uses a custom hostname specified in CustomHostname
	HostnameUseCustom HostnameHandlingMode = "use_custom"
)

type MetricsCollector

type MetricsCollector struct {
	// contains filtered or unexported fields
}

MetricsCollector collects and provides metrics for the reverse proxy.

func NewMetricsCollector

func NewMetricsCollector() *MetricsCollector

NewMetricsCollector creates a new MetricsCollector.

func (*MetricsCollector) GetMetrics

func (m *MetricsCollector) GetMetrics() map[string]interface{}

GetMetrics returns the collected metrics.

func (*MetricsCollector) MetricsHandler

func (m *MetricsCollector) MetricsHandler() http.HandlerFunc

MetricsHandler returns an HTTP handler for metrics endpoint.

func (*MetricsCollector) RecordRequest

func (m *MetricsCollector) RecordRequest(backend string, start time.Time, statusCode int, err error, metadata ...map[string]string)

RecordRequest records a request to a backend.

func (*MetricsCollector) SetCircuitBreakerStateString

func (m *MetricsCollector) SetCircuitBreakerStateString(backend string, state string)

SetCircuitBreakerStateString sets the status of a circuit breaker with an explicit state string.

func (*MetricsCollector) SetCircuitBreakerStatus

func (m *MetricsCollector) SetCircuitBreakerStatus(backend string, isOpen bool)

SetCircuitBreakerStatus sets the status of a circuit breaker.

type OverallHealthStatus added in v1.2.0

type OverallHealthStatus struct {
	Healthy           bool                     `json:"healthy"`
	TotalBackends     int                      `json:"total_backends"`
	HealthyBackends   int                      `json:"healthy_backends"`
	UnhealthyBackends int                      `json:"unhealthy_backends"`
	CircuitOpenCount  int                      `json:"circuit_open_count"`
	LastCheck         time.Time                `json:"last_check"`
	BackendDetails    map[string]*HealthStatus `json:"backend_details,omitempty"`
}

OverallHealthStatus represents the overall health status of the service.

type PathMatcher

type PathMatcher struct {
	// contains filtered or unexported fields
}

PathMatcher helps determine which backend should handle a request based on path patterns. It maintains a mapping of backend IDs to URL path patterns and provides methods to register patterns and find matching backends for incoming requests.

func NewPathMatcher

func NewPathMatcher() *PathMatcher

NewPathMatcher creates a new PathMatcher instance with initialized storage. This is used to create a fresh path matcher for route registration.

func (*PathMatcher) AddRoutePattern

func (pm *PathMatcher) AddRoutePattern(backendID, pattern string)

AddRoutePattern adds a path pattern that should be routed to the specified backend. Multiple patterns can be registered for the same backend.

Parameters:

  • backendID: The identifier of the backend service to route to
  • pattern: The URL path pattern to match (e.g., "/api/users")

func (*PathMatcher) MatchBackend

func (pm *PathMatcher) MatchBackend(path string) string

MatchBackend determines which backend should handle the given path. It checks if the path starts with any of the registered patterns and returns the matching backend ID. If multiple patterns match, the first match wins.

Parameters:

  • path: The request path to match against registered patterns

Returns:

  • The matching backendID or empty string if no match is found

type PathRewritingConfig added in v1.1.2

type PathRewritingConfig struct {
	// StripBasePath removes the specified base path from all requests before forwarding to backends
	StripBasePath string `json:"strip_base_path" yaml:"strip_base_path" toml:"strip_base_path" env:"STRIP_BASE_PATH"`

	// BasePathRewrite replaces the base path with a new path for all requests
	BasePathRewrite string `json:"base_path_rewrite" yaml:"base_path_rewrite" toml:"base_path_rewrite" env:"BASE_PATH_REWRITE"`

	// EndpointRewrites defines per-endpoint path rewriting rules
	EndpointRewrites map[string]EndpointRewriteRule `json:"endpoint_rewrites" yaml:"endpoint_rewrites" toml:"endpoint_rewrites"`
}

PathRewritingConfig defines configuration for path rewriting rules.

type ResponseInfo added in v1.1.5

type ResponseInfo struct {
	StatusCode   int               `json:"statusCode"`
	Headers      map[string]string `json:"headers,omitempty"`
	Body         string            `json:"body,omitempty"`
	BodySize     int64             `json:"bodySize"`
	ResponseTime time.Duration     `json:"responseTime"`
	Error        string            `json:"error,omitempty"`
}

ResponseInfo contains information about a backend response.

type RetryConfig

type RetryConfig struct {
	Enabled              bool          `json:"enabled" yaml:"enabled"`
	MaxRetries           int           `json:"max_retries" yaml:"max_retries"`
	BaseDelay            time.Duration `json:"base_delay" yaml:"base_delay"`
	MaxDelay             time.Duration `json:"max_delay" yaml:"max_delay"`
	Jitter               float64       `json:"jitter" yaml:"jitter"`
	Timeout              time.Duration `json:"timeout" yaml:"timeout"`
	RetryableStatusCodes []int         `json:"retryable_status_codes" yaml:"retryable_status_codes"`
}

RetryConfig provides configuration for the retry policy.

type RetryFunc

type RetryFunc func(ctx context.Context) (interface{}, int, error)

RetryFunc represents a function that can be retried.

type RetryPolicy

type RetryPolicy struct {
	// MaxRetries is the maximum number of retries to attempt.
	MaxRetries int
	// BaseDelay is the base delay between retries.
	BaseDelay time.Duration
	// MaxDelay is the maximum delay between retries.
	MaxDelay time.Duration
	// Jitter is the amount of randomness to add to the delay.
	Jitter float64
	// RetryableStatusCodes is a list of status codes that should trigger a retry.
	RetryableStatusCodes map[int]bool
	// Timeout is the timeout for each attempt.
	Timeout time.Duration
}

RetryPolicy defines the retry behavior for failing requests.

func DefaultRetryPolicy

func DefaultRetryPolicy() RetryPolicy

DefaultRetryPolicy returns a default retry policy.

func (RetryPolicy) CalculateBackoff

func (p RetryPolicy) CalculateBackoff(attempt int) time.Duration

CalculateBackoff calculates the backoff duration for a retry attempt.

func (RetryPolicy) ShouldRetry

func (p RetryPolicy) ShouldRetry(statusCode int) bool

ShouldRetry returns true if the status code should trigger a retry.

func (RetryPolicy) WithBaseDelay

func (p RetryPolicy) WithBaseDelay(baseDelay time.Duration) RetryPolicy

WithBaseDelay sets the base delay.

func (RetryPolicy) WithJitter

func (p RetryPolicy) WithJitter(jitter float64) RetryPolicy

WithJitter sets the jitter.

func (RetryPolicy) WithMaxDelay

func (p RetryPolicy) WithMaxDelay(maxDelay time.Duration) RetryPolicy

WithMaxDelay sets the maximum delay.

func (RetryPolicy) WithMaxRetries

func (p RetryPolicy) WithMaxRetries(maxRetries int) RetryPolicy

WithMaxRetries sets the maximum number of retries.

func (RetryPolicy) WithRetryableStatusCodes

func (p RetryPolicy) WithRetryableStatusCodes(codes ...int) RetryPolicy

WithRetryableStatusCodes sets the status codes that should trigger a retry.

func (RetryPolicy) WithTimeout

func (p RetryPolicy) WithTimeout(timeout time.Duration) RetryPolicy

WithTimeout sets the timeout for each attempt.

type ReverseProxyConfig

type ReverseProxyConfig struct {
	BackendServices        map[string]string               `json:"backend_services" yaml:"backend_services" toml:"backend_services" env:"BACKEND_SERVICES"`
	Routes                 map[string]string               `json:"routes" yaml:"routes" toml:"routes" env:"ROUTES"`
	RouteConfigs           map[string]RouteConfig          `json:"route_configs" yaml:"route_configs" toml:"route_configs"`
	DefaultBackend         string                          `json:"default_backend" yaml:"default_backend" toml:"default_backend" env:"DEFAULT_BACKEND"`
	CircuitBreakerConfig   CircuitBreakerConfig            `json:"circuit_breaker" yaml:"circuit_breaker" toml:"circuit_breaker"`
	BackendCircuitBreakers map[string]CircuitBreakerConfig `json:"backend_circuit_breakers" yaml:"backend_circuit_breakers" toml:"backend_circuit_breakers"`
	CompositeRoutes        map[string]CompositeRoute       `json:"composite_routes" yaml:"composite_routes" toml:"composite_routes"`
	TenantIDHeader         string                          `json:"tenant_id_header" yaml:"tenant_id_header" toml:"tenant_id_header" env:"TENANT_ID_HEADER" default:"X-Tenant-ID"`
	RequireTenantID        bool                            `json:"require_tenant_id" yaml:"require_tenant_id" toml:"require_tenant_id" env:"REQUIRE_TENANT_ID"`
	CacheEnabled           bool                            `json:"cache_enabled" yaml:"cache_enabled" toml:"cache_enabled" env:"CACHE_ENABLED"`
	CacheTTL               time.Duration                   `json:"cache_ttl" yaml:"cache_ttl" toml:"cache_ttl" env:"CACHE_TTL"`
	RequestTimeout         time.Duration                   `json:"request_timeout" yaml:"request_timeout" toml:"request_timeout" env:"REQUEST_TIMEOUT"`
	MetricsEnabled         bool                            `json:"metrics_enabled" yaml:"metrics_enabled" toml:"metrics_enabled" env:"METRICS_ENABLED"`
	MetricsPath            string                          `json:"metrics_path" yaml:"metrics_path" toml:"metrics_path" env:"METRICS_PATH"`
	MetricsEndpoint        string                          `json:"metrics_endpoint" yaml:"metrics_endpoint" toml:"metrics_endpoint" env:"METRICS_ENDPOINT"`
	HealthCheck            HealthCheckConfig               `json:"health_check" yaml:"health_check" toml:"health_check"`
	// BackendConfigs defines per-backend configurations including path rewriting and header rewriting
	BackendConfigs map[string]BackendServiceConfig `json:"backend_configs" yaml:"backend_configs" toml:"backend_configs"`

	// Debug endpoints configuration
	DebugEndpoints DebugEndpointsConfig `json:"debug_endpoints" yaml:"debug_endpoints" toml:"debug_endpoints"`

	// Dry-run configuration
	DryRun DryRunConfig `json:"dry_run" yaml:"dry_run" toml:"dry_run"`

	// Feature flag configuration
	FeatureFlags FeatureFlagsConfig `json:"feature_flags" yaml:"feature_flags" toml:"feature_flags"`
}

ReverseProxyConfig provides configuration options for the ReverseProxyModule.

type ReverseProxyModule

type ReverseProxyModule struct {
	// contains filtered or unexported fields
}

ReverseProxyModule provides a modular reverse proxy implementation with support for multiple backends, composite routes that combine responses from different backends, and tenant-specific routing configurations.

The module implements the following interfaces:

  • modular.Module: Basic module lifecycle
  • modular.Configurable: Configuration management
  • modular.ServiceAware: Service dependency management
  • modular.TenantAwareModule: Tenant lifecycle management
  • modular.Startable: Startup logic
  • modular.Stoppable: Shutdown logic

Key features include:

  • Multi-backend proxy routing with health checks
  • Composite responses combining multiple backend calls
  • Circuit breakers for fault tolerance
  • Response caching for performance optimization
  • Tenant-aware routing and configuration
  • Request/response transformation pipelines
  • Comprehensive metrics collection
  • Path-based and header-based routing rules

func NewModule

func NewModule() *ReverseProxyModule

NewModule creates a new ReverseProxyModule with default settings. This is the primary constructor for the reverseproxy module and should be used when registering the module with the application.

The module initializes with:

  • Optimized HTTP client with connection pooling
  • Circuit breakers for each backend
  • Response caching infrastructure
  • Metrics collection (if enabled)
  • Thread-safe data structures for concurrent access

Example:

app.RegisterModule(reverseproxy.NewModule())

func (*ReverseProxyModule) AddBackend added in v1.3.3

func (m *ReverseProxyModule) AddBackend(backendID, serviceURL string) error

AddBackend dynamically adds a new backend to the module at runtime and emits an event. It updates the configuration, creates the proxy, and (optionally) registers a default route if one matching the backend name does not already exist.

func (*ReverseProxyModule) AddBackendRoute

func (m *ReverseProxyModule) AddBackendRoute(backendID, routePattern string) error

AddBackendRoute registers a new route for a specific backend. It allows dynamically adding routes to the reverse proxy after initialization.

func (*ReverseProxyModule) AddCompositeRoute

func (m *ReverseProxyModule) AddCompositeRoute(pattern string, backends []string, strategy string)

AddCompositeRoute adds a composite route that combines responses from multiple backends. The strategy parameter determines how the responses are combined.

func (*ReverseProxyModule) Constructor

func (m *ReverseProxyModule) Constructor() modular.ModuleConstructor

Constructor returns a ModuleConstructor function that initializes the module with the required services. It expects a service that implements the routerService interface to register routes with.

func (*ReverseProxyModule) EmitEvent added in v1.3.1

func (m *ReverseProxyModule) EmitEvent(ctx context.Context, event cloudevents.Event) error

EmitEvent implements the ObservableModule interface. This allows the reverseproxy module to emit events that other modules or observers can receive.

func (*ReverseProxyModule) GetBackendHealthStatus added in v1.1.2

func (m *ReverseProxyModule) GetBackendHealthStatus(backendID string) (*HealthStatus, bool)

GetBackendHealthStatus returns the health status of a specific backend.

func (*ReverseProxyModule) GetConfig

func (m *ReverseProxyModule) GetConfig() *ReverseProxyConfig

GetConfig returns the module's configuration.

func (*ReverseProxyModule) GetHealthStatus added in v1.1.2

func (m *ReverseProxyModule) GetHealthStatus() map[string]*HealthStatus

GetHealthStatus returns the health status of all backends.

func (*ReverseProxyModule) GetOverallHealthStatus added in v1.2.0

func (m *ReverseProxyModule) GetOverallHealthStatus(includeDetails bool) *OverallHealthStatus

GetOverallHealthStatus returns the overall health status of all backends.

func (*ReverseProxyModule) GetRegisteredEventTypes added in v1.3.1

func (m *ReverseProxyModule) GetRegisteredEventTypes() []string

GetRegisteredEventTypes implements the ObservableModule interface. Returns all event types that this reverseproxy module can emit.

func (*ReverseProxyModule) Init

Init initializes the module with the provided application. It retrieves the module's configuration and sets up the internal data structures for each configured backend, including tenant-specific configurations.

func (*ReverseProxyModule) IsHealthCheckEnabled added in v1.1.2

func (m *ReverseProxyModule) IsHealthCheckEnabled() bool

IsHealthCheckEnabled returns whether health checking is enabled.

func (*ReverseProxyModule) Name

func (m *ReverseProxyModule) Name() string

Name returns the name of the module. This is used by the modular framework to identify the module.

func (*ReverseProxyModule) OnTenantRegistered

func (m *ReverseProxyModule) OnTenantRegistered(tenantID modular.TenantID)

OnTenantRegistered is called when a new tenant is registered with the application. Instead of immediately querying for tenant configuration, we store the tenant ID and defer configuration loading until the next appropriate phase to avoid deadlocks.

func (*ReverseProxyModule) OnTenantRemoved

func (m *ReverseProxyModule) OnTenantRemoved(tenantID modular.TenantID)

OnTenantRemoved is called when a tenant is removed from the application. It removes the tenant's configuration and any associated resources.

func (*ReverseProxyModule) ProvidesServices

func (m *ReverseProxyModule) ProvidesServices() []modular.ServiceProvider

ProvidesServices returns the services provided by this module. This module can provide a featureFlagEvaluator service if configured to do so, whether the evaluator was created internally or provided externally. This allows other modules to discover and use the evaluator.

func (*ReverseProxyModule) RegisterConfig

func (m *ReverseProxyModule) RegisterConfig(app modular.Application) error

RegisterConfig registers the module's configuration with the application. It also stores the provided app as a TenantApplication for later use with tenant-specific functionality.

func (*ReverseProxyModule) RegisterCustomEndpoint

func (m *ReverseProxyModule) RegisterCustomEndpoint(pattern string, mapping EndpointMapping)

RegisterCustomEndpoint adds a custom endpoint with a response transformer. This provides the most flexibility for combining and transforming responses from multiple backends using custom logic.

func (*ReverseProxyModule) RegisterObservers added in v1.3.1

func (m *ReverseProxyModule) RegisterObservers(subject modular.Subject) error

RegisterObservers implements the ObservableModule interface. This allows the reverseproxy module to register as an observer for events it's interested in.

func (*ReverseProxyModule) RemoveBackend added in v1.3.3

func (m *ReverseProxyModule) RemoveBackend(backendID string) error

RemoveBackend removes an existing backend at runtime and emits a backend.removed event.

func (*ReverseProxyModule) RequiresServices

func (m *ReverseProxyModule) RequiresServices() []modular.ServiceDependency

RequiresServices returns the services required by this module. The reverseproxy module requires a service that implements the routerService interface to register routes with, and optionally a http.Client and FeatureFlagEvaluator.

func (*ReverseProxyModule) SetHttpClient

func (m *ReverseProxyModule) SetHttpClient(client *http.Client)

SetHttpClient overrides the default HTTP client used by the module. This method can be used to customize the HTTP client with advanced settings such as custom timeouts, transport configurations, or for testing purposes. It should be called before the Start method.

Note: This also updates the transport for all existing reverse proxies. This method is retained for backward compatibility, but using the httpclient service is recommended for new code.

func (*ReverseProxyModule) Start

func (m *ReverseProxyModule) Start(ctx context.Context) error

Start sets up all routes for the module and registers them with the router. This includes backend routes, composite routes, and any custom endpoints.

func (*ReverseProxyModule) Stop

func (m *ReverseProxyModule) Stop(ctx context.Context) error

Stop performs any cleanup needed when stopping the module. This method gracefully shuts down active connections and resources.

type RouteConfig added in v1.1.4

type RouteConfig struct {
	// FeatureFlagID is the ID of the feature flag that controls whether this route uses the primary backend
	// If specified and the feature flag evaluates to false, requests will be routed to the alternative backend
	FeatureFlagID string `json:"feature_flag_id" yaml:"feature_flag_id" toml:"feature_flag_id" env:"FEATURE_FLAG_ID"`

	// AlternativeBackend specifies the backend to use when the feature flag is disabled
	// If FeatureFlagID is specified and evaluates to false, requests will be routed to this backend instead
	AlternativeBackend string `json:"alternative_backend" yaml:"alternative_backend" toml:"alternative_backend" env:"ALTERNATIVE_BACKEND"`

	// DryRun enables dry-run mode for this route, sending requests to both backends and comparing responses
	// When true, requests are sent to both the primary and alternative backends, but only the alternative backend's response is returned
	DryRun bool `json:"dry_run" yaml:"dry_run" toml:"dry_run" env:"DRY_RUN"`

	// DryRunBackend specifies the backend to compare against in dry-run mode
	// If not specified, uses the AlternativeBackend for comparison
	DryRunBackend string `json:"dry_run_backend" yaml:"dry_run_backend" toml:"dry_run_backend" env:"DRY_RUN_BACKEND"`
}

RouteConfig defines feature flag-controlled routing configuration for specific routes. This allows routes to be dynamically controlled by feature flags, with fallback to alternative backends.

type WeightedEvaluator added in v1.3.3

type WeightedEvaluator interface {
	FeatureFlagEvaluator
	// Weight returns the priority weight for this evaluator.
	// Lower values = higher priority. Default is 100 if not implemented.
	Weight() int
}

WeightedEvaluator is an optional interface that FeatureFlagEvaluator implementations can implement to specify their priority in the evaluation chain. Lower weight values indicate higher priority (evaluated first). Default weight for evaluators that don't implement this interface is 100. The built-in file evaluator has weight 1000 (lowest priority/last fallback).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL