Files
juanatsap d95c62bad4 refactor: remove outdated server design documentation
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.
2025-12-02 20:25:05 +00:00

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