d95c62bad4
Remove 557-line server-design.md from _go-learning/architecture - content is now covered in updated architecture documentation with real implementation examples and test coverage.
18 KiB
18 KiB
Middleware Chain Diagram
Middleware Execution Order
HTTP Request
│
▼
┌────────────────────────────────────────────────────────────┐
│ MIDDLEWARE CHAIN │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 1. Recovery Middleware │ │
│ │ - Catches panics │ │
│ │ - Logs stack trace │ │
│ │ - Returns 500 error │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 2. Logger Middleware │ │
│ │ - Logs request method, path, IP │ │
│ │ - Measures request duration │ │
│ │ - Logs response status │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 3. SecurityHeaders Middleware │ │
│ │ - Sets CSP header │ │
│ │ - Sets X-Frame-Options │ │
│ │ - Sets X-Content-Type-Options │ │
│ │ - Sets Referrer-Policy │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 4. PreferencesMiddleware │ │
│ │ - Reads preference cookies │ │
│ │ - Migrates old values │ │
│ │ - Stores in request context │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
└───────────────────────────┼────────────────────────────────┘
▼
┌───────────────┐
│ Router │
│ (ServeMux) │
└───────────────┘
│
▼
┌───────────────┐
│ Handler │
└───────────────┘
Detailed Flow
┌─────────────────────────────────────────────────────────────────┐
│ Request Processing Flow │
└─────────────────────────────────────────────────────────────────┘
Client Request: GET /?lang=es
│
▼
╔═══════════════════════════════════════════════════════════╗
║ RECOVERY MIDDLEWARE ║
╠═══════════════════════════════════════════════════════════╣
║ defer func() { ║
║ if err := recover(); err != nil { ║
║ log error + stack trace ║
║ http.Error(w, "Internal Server Error", 500) ║
║ } ║
║ }() ║
║ ║
║ next.ServeHTTP(w, r) ────────────────┐ ║
╚════════════════════════════════════════│══════════════════╝
│
▼
╔═══════════════════════════════════════════════════════════╗
║ LOGGER MIDDLEWARE ║
╠═══════════════════════════════════════════════════════════╣
║ start := time.Now() ║
║ log.Printf("[%s] %s %s", r.Method, r.URL.Path, r.RemoteAddr) ║
║ ║
║ wrapped := responseWriter wrapper ║
║ next.ServeHTTP(wrapped, r) ──────────┐ ║
║ │ ║
║ duration := time.Since(start) │ ║
║ log.Printf("Completed in %v (status: %d)", duration, status) ║
╚═════════════════════════════════════════│════════════════╝
│
▼
╔═══════════════════════════════════════════════════════════╗
║ SECURITY HEADERS MIDDLEWARE ║
╠═══════════════════════════════════════════════════════════╣
║ w.Header().Set("Content-Security-Policy", CSP_POLICY) ║
║ w.Header().Set("X-Frame-Options", "DENY") ║
║ w.Header().Set("X-Content-Type-Options", "nosniff") ║
║ w.Header().Set("Referrer-Policy", "strict-origin") ║
║ ║
║ next.ServeHTTP(w, r) ────────────────┐ ║
╚════════════════════════════════════════│══════════════════╝
│
▼
╔═══════════════════════════════════════════════════════════╗
║ PREFERENCES MIDDLEWARE ║
╠═══════════════════════════════════════════════════════════╣
║ // Read cookies ║
║ prefs := &Preferences{ ║
║ CVLength: getCookie(r, "cv-length", "short"), ║
║ CVIcons: getCookie(r, "cv-icons", "show"), ║
║ CVLanguage: getCookie(r, "cv-language", "en"), ║
║ CVTheme: getCookie(r, "cv-theme", "default"), ║
║ ColorTheme: getCookie(r, "color-theme", "light"), ║
║ } ║
║ ║
║ // Migrate old values ║
║ if prefs.CVLength == "extended" { ║
║ prefs.CVLength = "long" ║
║ } ║
║ ║
║ // Store in context ║
║ ctx := context.WithValue(r.Context(), PreferencesKey, prefs) ║
║ next.ServeHTTP(w, r.WithContext(ctx)) ───┐ ║
╚═════════════════════════════════════════════│════════════╝
│
▼
┌──────────────────┐
│ ROUTER HANDLER │
│ │
│ Matches route │
│ Calls handler │
└──────────────────┘
│
▼
┌──────────────────┐
│ HANDLER FUNC │
│ │
│ Processes req │
│ Returns resp │
└──────────────────┘
Route-Specific Middleware
┌────────────────────────────────────────────────────────────────┐
│ Route-Specific Middleware Example │
│ (PDF Export Endpoint) │
└────────────────────────────────────────────────────────────────┘
Global Middleware Chain (all routes)
│
├─ Recovery
├─ Logger
├─ SecurityHeaders
└─ PreferencesMiddleware
│
▼
┌─────────────────────────────────────────┐
│ Router (ServeMux) │
│ │
│ /export/pdf → pdfHandler (protected) │
│ │ │
│ ▼ │
│ ┌───────────────────────────┐ │
│ │ Route-Specific Chain │ │
│ │ │ │
│ │ 1. OriginChecker │ │
│ │ └─ Verify same origin│ │
│ │ │ │
│ │ 2. RateLimiter │ │
│ │ └─ 3 req/min per IP │ │
│ │ │ │
│ │ 3. ExportPDF Handler │ │
│ │ └─ Generate PDF │ │
│ └───────────────────────────┘ │
└─────────────────────────────────────────┘
Middleware Wrapping Pattern
// Middleware function signature
type Middleware func(http.Handler) http.Handler
// Wrapping example
handler := routes.Setup(cvHandler, healthHandler)
// Returns:
// Recovery(
// Logger(
// SecurityHeaders(
// PreferencesMiddleware(mux)
// )
// )
// )
// Execution flow (unwraps from outside to inside):
Request
↓ enters Recovery
↓ enters Logger
↓ enters SecurityHeaders
↓ enters PreferencesMiddleware
↓ enters mux/handler
↓ handler processes
↑ exits PreferencesMiddleware
↑ exits SecurityHeaders
↑ exits Logger (logs duration)
↑ exits Recovery
↑
Response
Context Flow
┌─────────────────────────────────────────────────────────────┐
│ Context Values Through Middleware │
└─────────────────────────────────────────────────────────────┘
Initial Request Context
│
├─ Empty context.Background()
│
▼
PreferencesMiddleware
│
├─ Reads cookies
├─ Creates Preferences struct
└─ Adds to context
│
└─→ ctx = context.WithValue(r.Context(), PreferencesKey, prefs)
│
▼
┌──────────────────────────────────────┐
│ Modified Request Context │
│ │
│ PreferencesKey → &Preferences{ │
│ CVLength: "long", │
│ CVIcons: "show", │
│ CVLanguage: "es", │
│ CVTheme: "default", │
│ ColorTheme: "light", │
│ } │
└──────────────────────────────────────┘
│
▼
Handler receives request with enriched context
│
├─→ prefs := middleware.GetPreferences(r)
│ // Retrieves from context
│
└─→ lang := middleware.GetLanguage(r)
// Helper that calls GetPreferences
Error Handling in Middleware
┌────────────────────────────────────────────────────────────┐
│ Error Handling Flow │
└────────────────────────────────────────────────────────────┘
Recovery Middleware
│
│ Normal Flow:
│ ┌─────────────────────────────────────┐
│ │ next.ServeHTTP(w, r) │
│ │ ↓ │
│ │ Handler processes successfully │
│ │ ↓ │
│ │ Returns response │
│ └─────────────────────────────────────┘
│
│ Panic Flow:
│ ┌─────────────────────────────────────┐
│ │ next.ServeHTTP(w, r) │
│ │ ↓ │
│ │ Handler panics! │
│ │ ↓ │
│ │ defer recover() catches it │
│ │ ↓ │
│ │ log.Printf("PANIC: %v\\n%s", │
│ │ err, debug.Stack()) │
│ │ ↓ │
│ │ http.Error(w, "Internal Error", 500)│
│ └─────────────────────────────────────┘
│
▼
Response to client
Performance Characteristics
Middleware Performance Impact (per request):
Recovery: ~10 ns (defer overhead)
Logger: ~100 μs (time measurements, string formatting)
SecurityHeaders: ~50 ns (header setting)
Preferences: ~200 μs (cookie parsing, context creation)
Total overhead: ~350 μs per request
Handler time: ~1-5 ms (template rendering)
Total request: ~1.5-5.5 ms
Related Diagrams
- System Architecture - Overall system design
- Request Flow - Complete HTTP request lifecycle
- Error Handling - Error propagation