Skip to main content

Overview

The timeout middleware enforces a maximum duration for request processing. If a handler takes too long, the request is cancelled and an error response is returned.

Installation

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

Quick Start

app := mizu.New()

// 30 second timeout
app.Use(timeout.New(30 * time.Second))

Configuration

OptionTypeDefaultDescription
Timeouttime.Duration30sMaximum request duration
ErrorHandlerfunc(*mizu.Ctx) error-Custom timeout handler
ErrorMessagestring"Service Unavailable"Error message

Examples

Basic Timeout

app.Use(timeout.New(30 * time.Second))

Custom Error Handler

app.Use(timeout.WithOptions(timeout.Options{
    Timeout: 10 * time.Second,
    ErrorHandler: func(c *mizu.Ctx) error {
        return c.JSON(504, map[string]string{
            "error": "Request timed out",
        })
    },
}))

Different Timeouts Per Route

// Global: 30s
app.Use(timeout.New(30 * time.Second))

// Slow endpoint: 5 minutes
app.Post("/export", exportHandler, timeout.New(5*time.Minute))

// Fast endpoint: 5 seconds
app.Get("/health", healthHandler, timeout.New(5*time.Second))

Check Context in Handler

app.Get("/data", func(c *mizu.Ctx) error {
    for i := 0; i < 100; i++ {
        select {
        case <-c.Context().Done():
            return c.Context().Err() // Timeout reached
        default:
            processItem(i)
        }
    }
    return c.JSON(200, results)
})

API Reference

func New(timeout time.Duration) mizu.Middleware
func WithOptions(opts Options) mizu.Middleware

How It Works

  1. Creates a context with deadline
  2. Runs handler in goroutine
  3. Returns whichever completes first:
    • Handler completion
    • Timeout expiration (returns 503)

Technical Details

Implementation

The timeout middleware uses Go’s context.WithTimeout to enforce request deadlines:
  1. Context Creation: Creates a context with deadline using context.WithTimeout(c.Context(), opts.Timeout)
  2. Request Update: Replaces the request’s context with the timeout context
  3. Goroutine Execution: Runs the next handler in a separate goroutine to enable timeout detection
  4. Channel Communication: Uses a buffered channel to receive the handler’s result
  5. Select Statement: Races between handler completion and context cancellation
    • If handler completes first, returns the result
    • If timeout occurs first, calls error handler or returns 503 Service Unavailable

Default Values

  • Timeout Duration: 30 seconds (when Timeout <= 0)
  • Error Message: “Service Unavailable”
  • HTTP Status: 503 Service Unavailable (when no custom error handler is provided)

Concurrency Safety

The middleware safely handles concurrent requests by:
  • Using a buffered channel (size 1) to prevent goroutine leaks
  • Properly deferring the context cancel function to release resources
  • Isolating each request’s timeout context

Best Practices

  • Set reasonable timeouts based on expected duration
  • Use longer timeouts for file uploads/downloads
  • Check context in long-running operations
  • Return 503 (Service Unavailable) on timeout

Testing

Test Cases

Test CaseDescriptionExpected Behavior
TestNew/times out slow requestHandler takes longer than timeout (200ms vs 50ms)Returns 503 Service Unavailable status
TestNew/allows fast requestHandler completes before timeoutReturns 200 OK with correct response body
TestWithOptions_ErrorHandlerCustom error handler is providedCalls custom error handler, returns 504 Gateway Timeout with JSON error
TestWithOptions_CustomMessageCustom error message is setReturns custom message “Request took too long” in response body
TestWithOptions_DefaultTimeoutNo timeout specified in optionsCreates middleware with 30 second default timeout
TestTimeout_ContextCancellationHandler checks context cancellationContext is cancelled when timeout occurs, handler can detect via c.Context().Done()