reverseproxy

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 26, 2025 License: MIT Imports: 16 Imported by: 0

README

Reverse Proxy Module

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
  • 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

Installation

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

Usage

package main

import (
	"github.com/GoCodeAlone/modular"
	"github.com/GoCodeAlone/modular/modules/chimux"
	"github.com/GoCodeAlone/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
  
  # 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

For detailed documentation and examples, see the DOCUMENTATION.md file.

License

MIT License

Documentation

Overview

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

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

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

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

Index

Constants

This section is empty.

Variables

This section is empty.

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 TenantIDFromRequest

func TenantIDFromRequest(tenantIDHeader string, req *http.Request) (string, bool)

TenantIDFromRequest extracts the tenant ID from an HTTP request if present. It returns the tenant ID as a string and a boolean indicating if a tenant ID was found.

Parameters:

  • tenantIDHeader: The name of the HTTP header containing the tenant ID
  • req: The HTTP request to extract the tenant ID from

Returns:

  • tenantID: The extracted tenant ID or an empty string if not found
  • found: True if a tenant ID was found, false otherwise

Types

type Backend

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

Backend represents a backend service configuration.

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 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 to prevent cascading failures when a backend service is unreliable or failing

func NewCircuitBreaker

func NewCircuitBreaker(failureThreshold int, resetTimeout time.Duration) *CircuitBreaker

NewCircuitBreaker creates a new circuit breaker with the specified failure threshold and reset timeout

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 and requests should not be processed

func (*CircuitBreaker) RecordFailure

func (cb *CircuitBreaker) RecordFailure()

RecordFailure records a failed request and potentially opens the circuit if the failure threshold is exceeded

func (*CircuitBreaker) RecordSuccess

func (cb *CircuitBreaker) RecordSuccess()

RecordSuccess records a successful request and resets the failure counter If the circuit was half-open, it closes the circuit

func (*CircuitBreaker) Reset

func (cb *CircuitBreaker) Reset()

Reset resets the circuit breaker to its initial state

type CircuitBreakerConfig

type CircuitBreakerConfig struct {
	// Enabled indicates if the circuit breaker is active
	Enabled bool `yaml:"enabled"`

	// FailureThreshold is the number of failures before opening the circuit
	FailureThreshold int `yaml:"failure_threshold"`

	// ResetTimeoutSeconds is the number of seconds to wait before trying a request
	// when the circuit is open
	ResetTimeoutSeconds int `yaml:"reset_timeout_seconds"`
}

CircuitBreakerConfig holds configuration options for a circuit breaker

type CircuitState

type CircuitState int

CircuitState represents the state of a circuit breaker

const (
	// CircuitClosed means the circuit is closed and requests flow normally
	CircuitClosed CircuitState = iota
	// CircuitOpen means the circuit is open and requests fail immediately
	CircuitOpen
	// CircuitHalfOpen means the circuit is testing if the backend has recovered
	CircuitHalfOpen
)

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 is the URL path pattern to match.
	// This uses the router's pattern matching syntax (typically chi router syntax).
	Pattern string `json:"pattern" yaml:"pattern"`

	// Backends is a list of backend identifiers to route to.
	// These should correspond to keys in the BackendServices map.
	Backends []string `json:"backends" yaml:"backends"`

	// Strategy determines how to combine responses from multiple backends.
	// Supported values include:
	// - "merge" - Merge JSON responses at the top level
	// - "select" - Select a specific backend's response
	// - "compare" - Compare responses from multiple backends
	// - "custom" - Use a custom response transformer
	Strategy string `json:"strategy" yaml:"strategy"`
}

CompositeRoute represents a route that combines responses from multiple backends. This allows for creating composite APIs that aggregate data from multiple sources.

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 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 ReverseProxyConfig

type ReverseProxyConfig struct {
	// BackendServices maps backend IDs to their service URLs.
	BackendServices map[string]string `yaml:"backend_services"`

	// DefaultBackend is the ID of the default backend to use.
	DefaultBackend string `yaml:"default_backend"`

	// Routes maps URL patterns to backend IDs.
	// These are used for direct proxying to a single backend.
	Routes map[string]string `yaml:"routes"`

	// CompositeRoutes maps URL patterns to composite route configurations.
	// These are used for routes that combine responses from multiple backends.
	CompositeRoutes map[string]CompositeRoute `yaml:"composite_routes"`

	// TenantIDHeader is the name of the HTTP header containing the tenant ID.
	TenantIDHeader string `yaml:"tenant_id_header"`

	// RequireTenantID indicates if requests must include a tenant ID.
	RequireTenantID bool `yaml:"require_tenant_id"`

	// CacheEnabled enables HTTP response caching for composite routes
	CacheEnabled bool `yaml:"cache_enabled"`

	// CacheTTL defines how long cached responses remain valid (in seconds)
	CacheTTL int `yaml:"cache_ttl"`

	// CircuitBreakerConfig holds the global circuit breaker configuration
	CircuitBreakerConfig CircuitBreakerConfig `yaml:"circuit_breaker"`

	// BackendCircuitBreakers defines per-backend circuit breaker configurations,
	// overriding the global settings for specific backends
	BackendCircuitBreakers map[string]CircuitBreakerConfig `yaml:"backend_circuit_breakers"`
}

ReverseProxyConfig defines the configuration for a reverse proxy module instance.

func (*ReverseProxyConfig) Validate

func (c *ReverseProxyConfig) Validate() error

Validate implements the modular.ConfigValidator interface to ensure the configuration is valid before the module is initialized.

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.

func NewModule

func NewModule() (*ReverseProxyModule, error)

NewModule creates a new ReverseProxyModule with default settings. It initializes the HTTP client with optimized connection pooling and timeouts, and prepares the internal data structures needed for routing.

func (*ReverseProxyModule) AddBackendRoute

func (m *ReverseProxyModule) AddBackendRoute(backendID, pattern string)

AddBackendRoute adds a route pattern for a specific backend. The route pattern is registered with the router during the Start phase. Example: AddBackendRoute("twitter", "/api/twitter/*") will route all requests to /api/twitter/* to the "twitter" backend.

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 handleFuncService interface to register routes with.

func (*ReverseProxyModule) GetConfig

func (m *ReverseProxyModule) GetConfig() *ReverseProxyConfig

GetConfig returns the module's configuration.

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) 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. It retrieves the tenant's configuration and stores it for use in routing.

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. Currently, this module does not provide any services.

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) RequiresServices

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

RequiresServices returns the services required by this module. The reverseproxy module requires a service that implements the handleFuncService interface to register routes with.

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.

func (*ReverseProxyModule) Start

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

Stop performs any cleanup needed when stopping the module. Currently, this is a no-op.

Jump to

Keyboard shortcuts

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