> ## 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.

# Mirror

> Request mirroring middleware for traffic shadowing.

## Overview

The `mirror` middleware duplicates requests to one or more target servers for traffic shadowing, testing, and analysis. Mirrored requests run asynchronously and don't affect the original response.

## Installation

```go theme={null}
import "github.com/go-mizu/mizu/middlewares/mirror"
```

## Quick Start

```go theme={null}
app := mizu.New()

// Mirror all traffic to staging server
app.Use(mirror.New("https://staging.example.com"))
```

## Configuration

| Option      | Type                           | Default | Description                        |
| ----------- | ------------------------------ | ------- | ---------------------------------- |
| `Targets`   | `[]Target`                     | -       | Mirror target URLs                 |
| `Timeout`   | `time.Duration`                | `5s`    | Timeout for mirrored requests      |
| `Async`     | `bool`                         | `true`  | Run mirror requests asynchronously |
| `CopyBody`  | `bool`                         | `true`  | Copy request body                  |
| `OnError`   | `func(string, error)`          | -       | Error callback                     |
| `OnSuccess` | `func(string, *http.Response)` | -       | Success callback                   |

## Examples

### Single Target

```go theme={null}
app.Use(mirror.New("https://staging.example.com"))

// All requests are mirrored to staging
app.Get("/api/users", usersHandler)
```

### Multiple Targets

```go theme={null}
app.Use(mirror.New(
    "https://staging.example.com",
    "https://analytics.example.com",
))
```

### Percentage-Based Mirroring

```go theme={null}
app.Use(mirror.WithOptions(mirror.Options{
    Targets: []mirror.Target{
        mirror.Percentage("https://staging.example.com", 50), // 50% of traffic
        mirror.Percentage("https://canary.example.com", 10),  // 10% of traffic
    },
}))
```

### Custom Timeout

```go theme={null}
app.Use(mirror.WithOptions(mirror.Options{
    Targets: []mirror.Target{
        {URL: "https://staging.example.com", Percentage: 100},
    },
    Timeout: 10 * time.Second,
}))
```

### Synchronous Mirroring

```go theme={null}
// Wait for mirror requests to complete
app.Use(mirror.WithOptions(mirror.Options{
    Targets: []mirror.Target{
        {URL: "https://backup.example.com", Percentage: 100},
    },
    Async: false,
}))
```

### Error Handling

```go theme={null}
app.Use(mirror.WithOptions(mirror.Options{
    Targets: []mirror.Target{
        {URL: "https://staging.example.com", Percentage: 100},
    },
    OnError: func(target string, err error) {
        log.Printf("Mirror to %s failed: %v", target, err)
        metrics.IncrementCounter("mirror_errors", target)
    },
}))
```

### Success Callback

```go theme={null}
app.Use(mirror.WithOptions(mirror.Options{
    Targets: []mirror.Target{
        {URL: "https://staging.example.com", Percentage: 100},
    },
    OnSuccess: func(target string, resp *http.Response) {
        log.Printf("Mirror to %s: status %d", target, resp.StatusCode)

        // Compare responses
        if resp.StatusCode >= 500 {
            alertOps("Staging returned error")
        }
    },
}))
```

### Response Comparison

```go theme={null}
var mu sync.Mutex
var prodResponses = make(map[string]int)
var stagingResponses = make(map[string]int)

app.Use(mirror.WithOptions(mirror.Options{
    Targets: []mirror.Target{
        {URL: "https://staging.example.com", Percentage: 100},
    },
    OnSuccess: func(target string, resp *http.Response) {
        mu.Lock()
        defer mu.Unlock()
        stagingResponses[resp.Request.URL.Path] = resp.StatusCode
    },
}))

// Middleware to track production responses
app.Use(func(next mizu.Handler) mizu.Handler {
    return func(c *mizu.Ctx) error {
        err := next(c)
        // Track and compare...
        return err
    }
})
```

### Route-Specific Mirroring

```go theme={null}
// Only mirror specific routes
api := app.Group("/api")
api.Use(mirror.New("https://staging.example.com"))

// Other routes are not mirrored
app.Get("/health", healthHandler)
```

### Canary Testing

```go theme={null}
// Mirror small percentage to canary for testing
app.Use(mirror.WithOptions(mirror.Options{
    Targets: []mirror.Target{
        mirror.Percentage("https://canary.example.com", 5),
    },
    OnSuccess: func(target string, resp *http.Response) {
        if resp.StatusCode >= 500 {
            // Alert on canary errors
            alertCanaryError(target, resp.StatusCode)
        }
    },
    OnError: func(target string, err error) {
        alertCanaryError(target, -1)
    },
}))
```

### A/B Testing Analysis

```go theme={null}
app.Use(mirror.WithOptions(mirror.Options{
    Targets: []mirror.Target{
        {URL: "https://experiment-a.example.com", Percentage: 50},
        {URL: "https://experiment-b.example.com", Percentage: 50},
    },
    OnSuccess: func(target string, resp *http.Response) {
        // Log response times for analysis
        latency := resp.Header.Get("X-Response-Time")
        analytics.TrackLatency(target, latency)
    },
}))
```

### Skip Body for GET Requests

```go theme={null}
app.Use(mirror.WithOptions(mirror.Options{
    Targets: []mirror.Target{
        {URL: "https://staging.example.com", Percentage: 100},
    },
    CopyBody: false, // Don't copy body (faster for GET)
}))
```

## Target Structure

```go theme={null}
type Target struct {
    URL        string // Target base URL
    Percentage int    // Percentage of requests to mirror (0-100)
}
```

## Helper Functions

```go theme={null}
// Create target with percentage
mirror.Percentage("https://example.com", 50) // 50% of traffic
```

## Mirrored Request Headers

Each mirrored request includes:

* All original headers
* `X-Mirrored-From: <original-host>`

## API Reference

```go theme={null}
func New(targets ...string) mizu.Middleware
func WithOptions(opts Options) mizu.Middleware
func Percentage(url string, pct int) Target
```

## Use Cases

1. **Shadow Testing**: Test new versions with production traffic
2. **Load Testing**: Send copies to test infrastructure
3. **Analytics**: Duplicate to analytics services
4. **Backup**: Sync to backup systems
5. **Canary Deployment**: Route small percentage to canary

## Technical Details

### Implementation Architecture

The mirror middleware is built with the following core components:

1. **Request Cloning**: The middleware reads the original request body (if `CopyBody` is enabled) and stores it in memory to allow multiple sends without consuming the original request stream.

2. **HTTP Client**: A dedicated `http.Client` is created with the configured timeout and redirect policy (`http.ErrUseLastResponse` to prevent following redirects).

3. **Percentage-Based Sampling**: Uses a simple counter-based modulo operation to determine whether to mirror a request based on the configured percentage (e.g., 50% mirrors every other request).

4. **Asynchronous Execution**: When `Async` is true (default), mirrored requests are executed in separate goroutines, ensuring zero impact on the main request latency.

### Request Flow

1. Original request arrives at the middleware
2. If `CopyBody` is enabled and body exists, read body into memory and restore it to the original request
3. For each target:
   * Check percentage threshold using counter-based sampling
   * If threshold met, execute mirror request (async or sync based on configuration)
4. Continue to next middleware/handler with original request unchanged

### Mirror Request Details

Each mirrored request includes:

* All original HTTP headers copied verbatim
* `X-Mirrored-From` header set to original request host
* Same HTTP method as original request
* Same request URI (path + query string) appended to target base URL
* Request body (if `CopyBody` is enabled)

### Performance Characteristics

* **Async mode**: Zero latency impact on original request (default)
* **Sync mode**: Adds mirror request latency to original request processing
* **Memory overhead**: Proportional to request body size when `CopyBody` is enabled
* **Goroutine cost**: One goroutine per target per mirrored request in async mode

### Error Handling

* Mirror failures never affect the original request/response
* Errors are silently ignored unless `OnError` callback is provided
* HTTP client respects the configured timeout to prevent hanging requests
* Response bodies are properly closed to prevent resource leaks

## Best Practices

* Use async mode (default) to avoid latency
* Set appropriate timeouts
* Monitor error rates on mirror targets
* Use percentage-based mirroring for gradual rollouts
* Log and compare responses for validation
* Don't mirror to targets with side effects (unless intended)

## Testing

The mirror middleware includes comprehensive test coverage for all features:

| Test Case                        | Description                                   | Expected Behavior                                                           |
| -------------------------------- | --------------------------------------------- | --------------------------------------------------------------------------- |
| TestNew                          | Basic mirror functionality with single target | Request is successfully mirrored to target server                           |
| TestWithOptions\_Async           | Asynchronous mirroring behavior               | Original response returns immediately without waiting for mirror completion |
| TestWithOptions\_Percentage      | Percentage-based traffic sampling             | Approximately 50% of requests are mirrored when percentage is set to 50     |
| TestWithOptions\_MultipleTargets | Mirroring to multiple targets simultaneously  | All configured targets receive mirrored requests                            |
| TestWithOptions\_OnError         | Error callback invocation                     | OnError callback is called when mirror request fails                        |
| TestWithOptions\_OnSuccess       | Success callback invocation                   | OnSuccess callback is called with response when mirror succeeds             |
| TestWithOptions\_MirroredHeader  | X-Mirrored-From header injection              | Mirror request includes X-Mirrored-From header with original host           |
| TestPercentage                   | Percentage helper function                    | Returns Target with correct URL and percentage values                       |
| TestWithOptions\_CopyBody        | Request body copying enabled                  | Mirror request receives the same body as original request                   |
| TestWithOptions\_NoCopyBody      | Request body copying disabled                 | Mirror request is sent without body content                                 |
| TestNew\_MultipleTargets         | Multiple targets via New() helper             | All targets receive mirrored requests with async execution                  |

## Related Middlewares

* [proxy](/middlewares/proxy) - Reverse proxy
* [chaos](/middlewares/chaos) - Chaos engineering
