proxykit

package
v1.16.2 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2026 License: MIT Imports: 18 Imported by: 0

README

Proxy Kit

A production-grade reverse proxy library implemented in Go. It not only provides high performance and multiple load balancing strategies, but also supports dynamic route management, allowing you to add or remove backend servers at runtime via API — without restarting the service.


Core Features

  • Dynamic Service Discovery: Add or remove backend nodes in real-time through HTTP APIs.
  • High Performance Core: Built on net/http/httputil with deeply optimized connection pooling for effortless high-concurrency handling.
  • Rich Load Balancing Strategies: Includes Round Robin, The Least Connections, and IP Hash.
  • Active Health Checks: Automatically detects and isolates unhealthy nodes, and brings them back online once they recover.
  • Multi-route Support: Distribute traffic to different backend groups based on path prefixes.

Example of Usage

package main

import (
    "log"
    "net/http"
    "time"
    "github.com/Eric-Guo/sponge/pkg/proxykit"
)

func main() {
    prefixPath := "/proxy/"

    // 1. Create the route manager
    manager := proxykit.NewRouteManager()

    // 2. Initialize backend targets
    initialTargets := []string{"http://localhost:8081", "http://localhost:8082"}
    backends, _ := proxykit.ParseBackends(prefixPath, initialTargets)
    proxykit.StartHealthChecks(backends, proxykit.HealthCheckConfig{Interval: 5 * time.Second})
    balancer := proxykit.NewRoundRobin(backends)

    // Register route
    apiRoute, err := manager.AddRoute(prefixPath, balancer)
    if err != nil {
        log.Fatalf("Could not add initial route: %v", err)
    }

    // 3. Build standard library mux
    mux := http.NewServeMux()

    // 4. Register proxy handler (same as gin.Any)
    mux.Handle(prefixPath, apiRoute.Proxy)

    // 5. Management API (corresponds to /endpoints/...)
    mux.HandleFunc("/endpoints/add", manager.HandleAddBackends)
    mux.HandleFunc("/endpoints/remove", manager.HandleRemoveBackends)
    mux.HandleFunc("/endpoints/list", manager.HandleListBackends)
    mux.HandleFunc("/endpoints", manager.HandleGetBackend)

    // 6. Other normal routes
    mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"message": "pong"}`))
    })

    // 7. Start HTTP server
    log.Println("HTTP server with dynamic proxy started on http://localhost:8080")
    log.Printf("Proxying requests from %s*\n", prefixPath)
    log.Println("Management API available at /endpoints/*")

    err = http.ListenAndServe(":8080", mux)
    if err != nil {
        log.Fatal("ListenAndServe:", err)
    }
}

Management API Guide

After the proxy is started, you can manage backend services dynamically via the following APIs.

1. List all backends

Retrieve all backend nodes and their health status for the given route.

  • GET /endpoints/list?prefixPath=/api/
{
  "prefixPath": "/api/",
  "targets": [
    {"target": "http://localhost:8081", "healthy": true}
  ]
}
2. Add backend nodes

Dynamically scale out. New nodes will automatically enter the health check loop and start receiving traffic.

  • POST /endpoints/add

  • Body:

    {
      "prefixPath": "/api/",
      "targets": ["http://localhost:8083", "http://localhost:8084"]
    }
    
3. Remove backend nodes

Dynamically scale in. Health checks for removed nodes will stop automatically.

  • POST /endpoints/remove

  • Body:

    {
      "prefixPath": "/api/",
      "targets": ["http://localhost:8081"]
    }
    
4. Inspect a single backend node
  • GET /endpoints?prefixPath=/api/&target=http://localhost:8082
{
  "target": "http://localhost:8082",
  "healthy": true
}

Generated HTTP services

The existing HTTP templates expose proxy, upstream, rails, and expanded http.tls settings. These features are optional: proxying and upstream process startup are disabled by default, and Rails authentication is enabled by replacing rails.secretKeyBase: change-me and setting the cookie name and permitted user ID.

pkg/gin/proxy.RegisterFallback sends unmatched Gin routes through proxykit while keeping registered API routes local. It supports TCP or UNIX socket upstreams, optional h2c, forwarded headers, a custom 502 page, load balancing, and management endpoints. UNIX socket health checks use the same socket as request forwarding.

pkg/proxykit/cache caches explicitly public responses with a positive max-age, tracks Vary headers, honors ETags, and omits Set-Cookie on cache hits. Range and upgrade requests bypass the cache; private/no-store responses are not cached. NewSendfileHandler translates upstream X-Sendfile responses into file downloads.

Cache keys now retain separate request components, are limited to 8 KiB, and count toward Ristretto's byte budget. Stored keys are compared on retrieval so hash collisions cannot return another request's response. Oversized keys, including learned Vary keys, bypass lookup. The Vary lookup index is bounded and expires. This changes the proxykit/cache.CacheKey API from a numeric key to a struct; custom Cache implementations should use the struct as their map key.

The Gin fallback preserves middleware-owned X-Request-ID and X-Request-Start headers after hop-by-hop stripping and discards upstream request-ID overrides. Use httpsrv.WrapHandler with AddRequestID: true around Gin, setting TrustRequestIDHeader to the same trust policy as FallbackProxyConfig.ForwardHeaders.

Generated HTTP servers also support gzip, request start timestamps, body limits, access logs, and self-signed, Let's Encrypt, external, or remote API TLS modes. Environment variables and remote API headers remain string maps after make update-config. These capabilities require the updated Sponge library; when testing an unreleased checkout, use a local Go module replacement.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNoHealthyBackends = errors.New("no healthy backends available")
)

Functions

func AnyRelativePath

func AnyRelativePath(prefixPath string) string

func Chain

func Chain(h http.Handler, middlewares ...Middleware) http.Handler

Chain links multiple middlewares together to form a single http.Handler.

func DefaultTransport

func DefaultTransport() *http.Transport

DefaultTransport returns a http.Transport optimized for reverse proxy usage.

func SetLogger

func SetLogger(l *zap.Logger)

func StartHealthChecks

func StartHealthChecks(backends []*Backend, config HealthCheckConfig)

StartHealthChecks initiate backend health check for the backend server pool.

Types

type Backend

type Backend struct {
	URL *url.URL
	// contains filtered or unexported fields
}

Backend encapsulates the backend server information.

func NewBackend

func NewBackend(prefixPath string, u *url.URL) *Backend

NewBackend creates a new Backend instance.

func ParseBackends

func ParseBackends(prefixPath string, targets []string) ([]*Backend, error)

ParseBackends helper function: converts a list of URL strings into []*Backend

func (*Backend) DecrementActiveConns

func (b *Backend) DecrementActiveConns()

func (*Backend) GetActiveConns

func (b *Backend) GetActiveConns() int64

func (*Backend) IncrementActiveConns

func (b *Backend) IncrementActiveConns()

func (*Backend) IsHealthy

func (b *Backend) IsHealthy() bool

func (*Backend) ReverseProxy

func (b *Backend) ReverseProxy() *httputil.ReverseProxy

ReverseProxy exposes the underlying reverse proxy to allow custom configuration.

func (*Backend) SetHealthy

func (b *Backend) SetHealthy(healthy bool)

func (*Backend) StopHealthCheck

func (b *Backend) StopHealthCheck()

StopHealthCheck stops the health check goroutine associated with this backend.

type Balancer

type Balancer interface {
	Next(r *http.Request) (*Backend, error)
	GetBackends() []*Backend
	AddBackend(b *Backend)
	RemoveBackend(b *Backend)
}

Balancer is an interface that must be implemented for all load balancing strategies.

type HealthCheckConfig

type HealthCheckConfig struct {
	UnixSocket string        `json:"unixSocket,omitempty"`
	Interval   time.Duration `json:"interval"`
	Timeout    time.Duration `json:"timeout"`
}

HealthCheckConfig defined the configuration for health check.

type IPHash

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

func NewIPHash

func NewIPHash(backends []*Backend) *IPHash

func (*IPHash) AddBackend

func (h *IPHash) AddBackend(b *Backend)

func (*IPHash) GetBackends

func (h *IPHash) GetBackends() []*Backend

func (*IPHash) Next

func (h *IPHash) Next(r *http.Request) (*Backend, error)

func (*IPHash) RemoveBackend

func (h *IPHash) RemoveBackend(b *Backend)

type LeastConnections

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

func NewLeastConnections

func NewLeastConnections(backends []*Backend) *LeastConnections

func (*LeastConnections) AddBackend

func (lc *LeastConnections) AddBackend(b *Backend)

func (*LeastConnections) GetBackends

func (lc *LeastConnections) GetBackends() []*Backend

func (*LeastConnections) Next

func (lc *LeastConnections) Next(_ *http.Request) (*Backend, error)

func (*LeastConnections) RemoveBackend

func (lc *LeastConnections) RemoveBackend(b *Backend)

type ManagementRequest

type ManagementRequest struct {
	PrefixPath  string            `json:"prefixPath"`
	Targets     []string          `json:"targets"`
	HealthCheck HealthCheckConfig `json:"healthCheck"`
}

ManagementRequest is for the management API.

type Middleware

type Middleware func(http.Handler) http.Handler

Middleware is a function that takes a http.Handler and returns a http.Handler, used to build a middleware chain.

type Proxy

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

Proxy is a reverse proxy that implements the http.Handler interface.

func NewProxy

func NewProxy(balancer Balancer) (*Proxy, error)

NewProxy creates a new reverse proxy instance.

func (*Proxy) ServeHTTP

func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP handles incoming HTTP requests and forwards them to the backend selected by the load balancer.

type RoundRobin

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

func NewRoundRobin

func NewRoundRobin(backends []*Backend) *RoundRobin

func (*RoundRobin) AddBackend

func (r *RoundRobin) AddBackend(b *Backend)

AddBackend implement the Balancer interface

func (*RoundRobin) GetBackends

func (r *RoundRobin) GetBackends() []*Backend

GetBackends implement the Balancer interface

func (*RoundRobin) Next

func (r *RoundRobin) Next(_ *http.Request) (*Backend, error)

func (*RoundRobin) RemoveBackend

func (r *RoundRobin) RemoveBackend(b *Backend)

RemoveBackend implement the Balancer interface

type Route

type Route struct {
	PrefixPath string
	Backends   []*Backend
	Balancer   Balancer
	Proxy      *Proxy
	// contains filtered or unexported fields
}

Route holds all components for a specific routing rule.

type RouteManager

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

RouteManager manages all routing rules.

func NewRouteManager

func NewRouteManager() *RouteManager

NewRouteManager creates a new manager.

func (*RouteManager) AddRoute

func (m *RouteManager) AddRoute(prefixPath string, balancer Balancer) (*Route, error)

AddRoute adds a new routing rule and configures its proxy to strip the given prefix.

func (*RouteManager) GetRoute

func (m *RouteManager) GetRoute(prefixPath string) (*Route, bool)

GetRoute safely retrieves a route.

func (*RouteManager) HandleAddBackends

func (m *RouteManager) HandleAddBackends(w http.ResponseWriter, r *http.Request)

HandleAddBackends handles the HTTP request to add new backends to a route.

func (*RouteManager) HandleGetBackend

func (m *RouteManager) HandleGetBackend(w http.ResponseWriter, r *http.Request)

HandleGetBackend handles the HTTP request to get a backend in a route.

func (*RouteManager) HandleListBackends

func (m *RouteManager) HandleListBackends(w http.ResponseWriter, r *http.Request)

HandleListBackends handles the HTTP request to list backends in a route.

func (*RouteManager) HandleRemoveBackends

func (m *RouteManager) HandleRemoveBackends(w http.ResponseWriter, r *http.Request)

HandleRemoveBackends handles the HTTP request to remove backends from a route.

type SendfileHandler

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

SendfileHandler converts X-Sendfile headers into direct file responses when enabled.

func NewSendfileHandler

func NewSendfileHandler(enabled bool, next http.Handler) *SendfileHandler

NewSendfileHandler wraps the provided handler with X-Sendfile support.

func (*SendfileHandler) ServeHTTP

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

ServeHTTP sets up X-Sendfile translation when enabled before delegating to the next handler.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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