Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The proxy middleware provides reverse proxy functionality, forwarding requests to upstream servers. It supports load balancing, request modification, and response handling.

Installation

import "github.com/go-mizu/mizu/middlewares/proxy"

Quick Start

app := mizu.New()

// Forward all requests to upstream
app.Use(proxy.New("http://backend:8080"))

Configuration

OptionTypeDefaultDescription
Target*url.URLrequiredUpstream server URL
Rewritefunc(string) string-Path rewriter
ModifyRequestfunc(*http.Request)-Request modifier
ModifyResponsefunc(*http.Response) error-Response modifier
Transporthttp.RoundTripperDefaultHTTP transport
Timeouttime.Duration30sRequest timeout
PreserveHostboolfalseKeep original Host header
ErrorHandlerfunc(*mizu.Ctx, error) error-Error handler

Examples

Basic Proxy

app.Get("/api/*", nil, proxy.New("http://api-server:3000"))

Path Rewriting

app.Use(proxy.WithOptions(proxy.Options{
    Target: mustParseURL("http://backend:8080"),
    Rewrite: func(path string) string {
        return strings.TrimPrefix(path, "/api/v1")
    },
}))

Load Balancing

app.Use(proxy.Balancer([]string{
    "http://server1:8080",
    "http://server2:8080",
    "http://server3:8080",
}))

Request Modification

app.Use(proxy.WithOptions(proxy.Options{
    Target: mustParseURL("http://backend:8080"),
    ModifyRequest: func(req *http.Request) {
        req.Header.Set("X-Source", "gateway")
        req.Header.Del("Authorization") // Strip auth
    },
}))

Response Modification

app.Use(proxy.WithOptions(proxy.Options{
    Target: mustParseURL("http://backend:8080"),
    ModifyResponse: func(resp *http.Response) error {
        resp.Header.Set("X-Proxy", "mizu")
        return nil
    },
}))

Preserve Host Header

app.Use(proxy.WithOptions(proxy.Options{
    Target:       mustParseURL("http://backend:8080"),
    PreserveHost: true,
}))

Custom Timeout

app.Use(proxy.WithOptions(proxy.Options{
    Target:  mustParseURL("http://backend:8080"),
    Timeout: 60 * time.Second,
}))

API Reference

func New(target string) mizu.Middleware
func WithOptions(opts Options) mizu.Middleware
func Balancer(targets []string) mizu.Middleware

Headers Added

HeaderValue
X-Forwarded-ForClient IP chain
X-Forwarded-HostOriginal host
X-Forwarded-Protohttp or https

Technical Details

Request Flow

  1. URL Construction: The target URL is built by combining the upstream target with the request path. If a Rewrite function is provided, it transforms the path before joining.
  2. Path Joining: The singleJoiningSlash helper ensures proper path construction by handling slash prefixes/suffixes correctly, preventing issues like double slashes or missing slashes.
  3. Request Creation: A new HTTP request is created with the same method, context, and body as the original request.
  4. Header Propagation: All headers from the original request are copied to the proxy request using the copyHeaders function, which preserves multi-value headers.
  5. Forwarded Headers: The middleware automatically sets standard forwarding headers:
    • X-Forwarded-For: Appends client IP to any existing value (creates a chain)
    • X-Forwarded-Host: Sets the original host
    • X-Forwarded-Proto: Sets https or http based on TLS presence
  6. Host Header Handling: By default, the Host header is set to the upstream target’s host. When PreserveHost is true, the original Host header is preserved.
  7. Request Modification: If ModifyRequest is provided, it’s called before sending the request, allowing custom header manipulation or other modifications.
  8. HTTP Client Configuration:
    • Uses configurable transport (defaults to http.DefaultTransport)
    • Applies the specified timeout (defaults to 30 seconds)
    • Disables automatic redirect following (CheckRedirect returns ErrUseLastResponse)
  9. Response Handling: After receiving the upstream response:
    • ModifyResponse callback is invoked if configured
    • Response headers are copied to the client
    • Status code is written
    • Response body is streamed to the client using io.Copy
  10. Error Handling: Errors are passed to the custom ErrorHandler if configured, otherwise returns a 502 Bad Gateway response.

Load Balancer Implementation

The Balancer function implements simple round-robin load balancing:
  • Maintains a counter for selecting the next upstream
  • Uses modulo arithmetic to cycle through targets
  • Counter is not thread-safe but provides good-enough distribution for most use cases
  • Each request creates a new proxy middleware instance with the selected target

Performance Considerations

  • Streaming: Response body is streamed using io.Copy, avoiding buffering large responses in memory
  • Connection Reuse: Uses http.DefaultTransport by default, which includes connection pooling
  • Context Propagation: Request context is preserved, enabling timeout and cancellation propagation
  • Header Copying: Headers are copied efficiently without additional allocations for single-value headers

Best Practices

  1. Set Appropriate Timeouts: Configure timeouts based on your upstream service characteristics to prevent hanging requests.
  2. Error Handling: Implement custom error handlers for better error reporting and monitoring.
  3. Security: Be cautious with ModifyRequest to avoid exposing sensitive headers to untrusted upstreams.
  4. Path Rewriting: Use the Rewrite function for API versioning or routing transformations.
  5. Load Balancing: For production load balancing, consider implementing health checks and weighted distributions.

Testing

The proxy middleware includes comprehensive test coverage for all functionality:
Test CaseDescriptionExpected Behavior
TestNewBasic proxy functionalityForwards request to upstream, returns upstream response with headers
TestWithOptions_RewritePath rewriting with custom functionTransforms request path before forwarding (e.g., /users β†’ /api/v2/users)
TestWithOptions_ModifyRequestRequest modification callbackCustom headers are added to upstream request
TestWithOptions_ModifyResponseResponse modification callbackCustom headers are added to client response
TestWithOptions_PreserveHostHost header preservationOriginal Host header is preserved when PreserveHost is true
TestWithOptions_ErrorHandlerCustom error handlingCustom error handler is invoked on proxy failure
TestWithOptions_QueryStringQuery parameter forwardingQuery strings are preserved and forwarded to upstream
TestWithOptions_POSTPOST request handlingPOST requests with body are proxied correctly
TestNew_InvalidURLInvalid target URL validationPanics with descriptive error on invalid URL
TestWithOptions_NoTargetMissing target validationPanics when target is not provided
TestBalancerRound-robin load balancingDistributes requests across multiple upstreams in rotation
TestBalancer_EmptyEmpty balancer targets validationPanics when no targets are provided