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
| Option | Type | Default | Description |
|---|
Target | *url.URL | required | Upstream server URL |
Rewrite | func(string) string | - | Path rewriter |
ModifyRequest | func(*http.Request) | - | Request modifier |
ModifyResponse | func(*http.Response) error | - | Response modifier |
Transport | http.RoundTripper | Default | HTTP transport |
Timeout | time.Duration | 30s | Request timeout |
PreserveHost | bool | false | Keep original Host header |
ErrorHandler | func(*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
},
}))
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
| Header | Value |
|---|
X-Forwarded-For | Client IP chain |
X-Forwarded-Host | Original host |
X-Forwarded-Proto | http or https |
Technical Details
Request Flow
-
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.
-
Path Joining: The
singleJoiningSlash helper ensures proper path construction by handling slash prefixes/suffixes correctly, preventing issues like double slashes or missing slashes.
-
Request Creation: A new HTTP request is created with the same method, context, and body as the original request.
-
Header Propagation: All headers from the original request are copied to the proxy request using the
copyHeaders function, which preserves multi-value headers.
-
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
-
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.
-
Request Modification: If
ModifyRequest is provided, itβs called before sending the request, allowing custom header manipulation or other modifications.
-
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)
-
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
-
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
- 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
-
Set Appropriate Timeouts: Configure timeouts based on your upstream service characteristics to prevent hanging requests.
-
Error Handling: Implement custom error handlers for better error reporting and monitoring.
-
Security: Be cautious with
ModifyRequest to avoid exposing sensitive headers to untrusted upstreams.
-
Path Rewriting: Use the
Rewrite function for API versioning or routing transformations.
-
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 Case | Description | Expected Behavior |
|---|
TestNew | Basic proxy functionality | Forwards request to upstream, returns upstream response with headers |
TestWithOptions_Rewrite | Path rewriting with custom function | Transforms request path before forwarding (e.g., /users β /api/v2/users) |
TestWithOptions_ModifyRequest | Request modification callback | Custom headers are added to upstream request |
TestWithOptions_ModifyResponse | Response modification callback | Custom headers are added to client response |
TestWithOptions_PreserveHost | Host header preservation | Original Host header is preserved when PreserveHost is true |
TestWithOptions_ErrorHandler | Custom error handling | Custom error handler is invoked on proxy failure |
TestWithOptions_QueryString | Query parameter forwarding | Query strings are preserved and forwarded to upstream |
TestWithOptions_POST | POST request handling | POST requests with body are proxied correctly |
TestNew_InvalidURL | Invalid target URL validation | Panics with descriptive error on invalid URL |
TestWithOptions_NoTarget | Missing target validation | Panics when target is not provided |
TestBalancer | Round-robin load balancing | Distributes requests across multiple upstreams in rotation |
TestBalancer_Empty | Empty balancer targets validation | Panics when no targets are provided |