logo
0
0
WeChat Login
Henry Huang<hhh@rutcode.com>
Refactor: migrate to common.v3 packages and update dependencies

Middlewares Package

This package provides middleware components for both HTTP and gRPC services, organized by functionality. The package is structured into sub-packages to avoid naming conflicts and improve code organization.

Package Structure

middlewares/ ├── middlewares.go # Re-exports all public APIs for backward compatibility ├── httpmw/ # HTTP middleware (renamed from 'http' to avoid conflict with stdlib) │ ├── http.go # CORS, file type checking │ ├── http_pprof.go # Performance profiling │ └── http_prometheus.go # Prometheus metrics ├── ratelimitmw/ # Rate limiting middleware (wraps utils/ratelimit package) │ ├── ratelimit.go # Gin middleware handler │ ├── ratelimit_helper.go # Helper functions │ └── ratelimit_http.go # HTTP handler wrapper ├── circuitbreakermw/ # Circuit breaker middleware (wraps utils/circuitbreaker package) │ └── circuitbreaker.go # Gin and HTTP handler wrappers └── grpcmw/ # gRPC middleware ├── timeout.go # Timeout interceptor ├── error_handler.go # Error handling interceptor ├── logger.go # Logging interceptor ├── compression.go # Compression interceptor └── validation.go # Request validation interceptor

Note: Tracing utilities have been moved to utils/tracing/ as they are used by both HTTP and gRPC services. IP utilities have been moved to utils/iputil/ as they are general-purpose utilities.

Supported Features

HTTP (Gin)

  • CORS, PProf, Prometheus metrics
  • Timeout, Body Limit, Request Logger
  • Compression (gzip), Validation
  • Authentication/Authorization (JWT, API Key, Basic)
  • Request Deduplication (idempotency key)
  • Swagger/OpenAPI UI and spec serving
  • Response Caching (ETag, Cache-Control, Vary)

gRPC (Interceptors)

  • Timeout, Error Handling, Logging
  • Compression (server-level parameterization)
  • Validation
  • Authentication/Authorization
  • Request Deduplication
  • Response Caching

Package Naming Convention

To avoid conflicts with Go standard library packages, all sub-packages use descriptive suffixes:

  • httpmw - HTTP Middleware (avoids conflict with net/http)
  • grpcmw - gRPC Middleware (unified naming with HTTP middleware)
  • ratelimitmw - Rate Limiting Middleware (wraps utils/ratelimit package)
  • circuitbreakermw - Circuit Breaker Middleware (wraps utils/circuitbreaker package)

Note:

  • Tracing utilities are in utils/tracing/ (used by both HTTP and gRPC)
  • IP utilities are in utils/iputil/ (general-purpose utilities)

Package Relationships

Rate Limiting Architecture

The framework has two rate limiting packages with distinct responsibilities:

1. ratelimit/ (Root Package)

Location: trellis.tech/trellis/common.v3/middleware/ratelimit

Purpose: Core rate limiter implementation

Provides:

  • Limiter interface - Defines rate limiting behavior
  • TokenBucketLimiter - Token bucket algorithm implementation
  • KeyExtractor - Key extraction functions (IP, User ID, Path, etc.)
  • Config - Rate limiter configuration
  • gRPC interceptors - UnaryRateLimitInterceptor, StreamRateLimitInterceptor

Usage: Generic rate limiting that can be used in any context (HTTP, gRPC, etc.)

2. middlewares/ratelimitmw/ (Middleware Package)

Location: trellis.tech/trellis/framework.v0/middlewares/ratelimitmw

Purpose: HTTP middleware wrapper for rate limiting

Provides:

  • RateLimitConfig - Middleware-specific configuration (includes HTTP-specific options)
  • RateLimitHandler - Gin framework middleware
  • RateLimitHTTPHandler - Standard http.Handler wrapper
  • NewRateLimitComponents - Factory function to create rate limiting components

Usage: HTTP/Gin-specific rate limiting middleware

Dependencies: Uses utils/ratelimit package internally

3. middlewares/circuitbreakermw/ (Circuit Breaker Middleware)

Location: trellis.tech/trellis/framework.v0/middlewares/circuitbreakermw

Purpose: HTTP middleware wrapper for circuit breaking

Provides:

  • CircuitBreakerConfig - Middleware-specific configuration
  • CircuitBreakerHandler - Gin framework middleware
  • CircuitBreakerHTTPHandler - Standard http.Handler wrapper
  • NewCircuitBreakerComponents - Factory function to create circuit breaker components

Usage: HTTP/Gin-specific circuit breaker middleware

Dependencies: Uses utils/circuitbreaker package internally

Relationship Diagram

┌─────────────────────────────────┐ │ utils/ratelimit/ (Core Package) │ │ - Limiter interface │ │ - TokenBucketLimiter │ │ - KeyExtractor │ │ - gRPC interceptors │ └──────────────┬──────────────────┘ │ uses ▼ ┌─────────────────────────────────┐ │ middlewares/ratelimitmw/ │ │ - RateLimitConfig │ │ - RateLimitHandler (Gin) │ │ - RateLimitHTTPHandler │ │ - NewRateLimitComponents │ └─────────────────────────────────┘ ┌─────────────────────────────────────┐ │ utils/circuitbreaker/ (Core Package)│ │ - CircuitBreaker │ │ - Config │ │ - gRPC interceptors │ └──────────────┬──────────────────────┘ │ used by ▼ ┌─────────────────────────────────────┐ │ middlewares/circuitbreakermw/ │ │ - CircuitBreakerConfig │ │ - CircuitBreakerHandler (Gin) │ │ - CircuitBreakerHTTPHandler │ │ - NewCircuitBreakerComponents │ └─────────────────────────────────────┘

Usage Examples

Using Rate Limiting Middleware

import ( "trellis.tech/trellis/framework.v0/middlewares/ratelimitmw" "trellis.tech/trellis/common.v3/middleware/ratelimit" ) // Configure rate limiting in service config rateLimitConfig := &ratelimitmw.RateLimitConfig{ Enabled: true, Rate: 100, // 100 requests Period: "1s", // per second Burst: 150, // allow burst up to 150 KeyType: ratelimit.KeyTypeIP, // limit by IP address SkipPaths: []string{"/health", "/metrics"}, StatusCode: 429, Message: "rate limit exceeded", }

Using Core Rate Limiter (for gRPC)

import ( "google.golang.org/grpc" "trellis.tech/trellis/common.v3/middleware/ratelimit" ) // Create rate limiter config := ratelimit.NewConfig(100, time.Second) limiter := ratelimit.NewTokenBucketLimiter(config) // Create key extractor keyExtractor := ratelimit.NewKeyExtractor(ratelimit.KeyTypeIP, nil) // Add interceptors server := grpc.NewServer( grpc.UnaryInterceptor(ratelimit.UnaryRateLimitInterceptor(limiter, keyExtractor)), grpc.StreamInterceptor(ratelimit.StreamRateLimitInterceptor(limiter, keyExtractor)), )

Using HTTP Middleware

import ( "trellis.tech/trellis/framework.v0/middlewares/httpmw" ) // CORS middleware cors, err := httpmw.LoadCorsHandler(&httpmw.CorsConfig{ AllowOrigins: []string{"http://localhost:3000"}, AllowMethods: []string{"GET", "POST"}, }) // Prometheus metrics engine.Use(httpmw.RouterPromeHandler("my-service", &httpmw.PromeOpts{})) // Performance profiling httpmw.PprofHandler(&httpmw.PprofConfig{Enabled: true}, engine)

Using Circuit Breaker Middleware

import ( "trellis.tech/trellis/framework.v0/middlewares/circuitbreakermw" ) // Configure circuit breaker in service config circuitBreakerConfig := &circuitbreakermw.CircuitBreakerConfig{ Enabled: true, Name: "http-service", MaxRequests: 1, Interval: "60s", Timeout: "60s", ConsecutiveFailures: 5, SkipPaths: []string{"/health", "/metrics"}, StatusCode: 503, Message: "circuit breaker is open", } // The middleware will be automatically applied when CircuitBreakerConfig is set

Using IP Utilities

import ( "trellis.tech/trellis/framework.v0/utils/iputil" ) // Get local IP address ip, err := iputil.GetLocalIp() if err != nil { // Handle error }

Using Tracing

import ( "trellis.tech/trellis/common.v3/middleware/tracing" ) // Get trace ID from context traceID := tracing.TraceIDFromContext(ctx) // Tracing is automatically enabled for all HTTP and gRPC services // See utils/tracing/README.md for details

Using gRPC Middleware

import ( "google.golang.org/grpc" "trellis.tech/trellis/framework.v0/middlewares/grpcmw" ) // Create gRPC server with interceptors server := grpc.NewServer( grpc.UnaryInterceptor( grpcmw.UnaryTimeoutInterceptor(timeoutConfig), grpcmw.UnaryErrorHandlerInterceptor(), grpcmw.UnaryLoggerInterceptor(loggerConfig), ), )

Backward Compatibility

The middlewares.go file in the root of the middlewares package re-exports all public APIs, allowing existing code to continue using the old import path:

import "trellis.tech/trellis/framework.v0/middlewares" // These still work: cfg := &middlewares.RateLimitConfig{...} middlewares.NewRateLimitComponents(cfg) middlewares.LoadCorsHandler(...)

However, it's recommended to use the specific sub-packages directly for better clarity:

import "trellis.tech/trellis/framework.v0/middlewares/ratelimitmw" import "trellis.tech/trellis/framework.v0/middlewares/httpmw"

Key Types for Rate Limiting

  • KeyTypeIP - Rate limit by client IP address
  • KeyTypeUserID - Rate limit by user ID (from context)
  • KeyTypePath - Rate limit by request path
  • KeyTypeIPPath - Rate limit by IP + path combination
  • KeyTypeUserPath - Rate limit by user ID + path combination
  • KeyTypeCustom - Use custom extractor function

Testing

All sub-packages include comprehensive test coverage:

# Run tests for all middlewares go test ./middlewares/... # Run tests for specific sub-package go test ./middlewares/ratelimitmw go test ./middlewares/circuitbreakermw go test ./middlewares/httpmw go test ./middlewares/grpcmw

Migration Guide

If you're migrating from the old structure:

  1. Import paths: Update imports from middlewares to specific sub-packages

    • middlewares.RateLimitConfigratelimitmw.RateLimitConfig
    • middlewares.LoadCorsHandlerhttpmw.LoadCorsHandler
  2. Package aliases: You may need to add aliases to avoid conflicts

    import ( ratelimitmw "trellis.tech/trellis/framework.v0/middlewares/ratelimitmw" "trellis.tech/trellis/common.v3/middleware/ratelimit" )
  3. Type references: Update type references in your code

    • *middlewares.RateLimitConfig*ratelimitmw.RateLimitConfig
    • *middlewares.CorsConfig*httpmw.CorsConfig