package handler import ( "log" "net/http" "time" ) // Chain wraps h with each middleware in order (first applied outermost). func Chain(h http.Handler, mw ...func(http.Handler) http.Handler) http.Handler { for i := len(mw) - 1; i >= 0; i-- { h = mw[i](h) } return h } // LoggingMiddleware logs method, path, status code, and duration. func LoggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() lw := &loggingResponseWriter{ResponseWriter: w, code: http.StatusOK} next.ServeHTTP(lw, r) log.Printf("%s %s %d %s", r.Method, r.URL.RequestURI(), lw.code, time.Since(start)) }) } type loggingResponseWriter struct { http.ResponseWriter code int } func (lw *loggingResponseWriter) WriteHeader(code int) { lw.code = code lw.ResponseWriter.WriteHeader(code) } // SecurityHeadersMiddleware sets security-related HTTP response headers. func SecurityHeadersMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'none'; style-src 'self'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'") w.Header().Set("X-Frame-Options", "DENY") w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") w.Header().Set("Permissions-Policy", "camera=(), microphone=(), geolocation=()") next.ServeHTTP(w, r) }) }