Files
fn_registry/functions/infra/http_logger_middleware.go
T
egutierrez 852322a708 fix(http_logger): preservar Hijack y Flush para WebSocket y SSE
El responseWriter del logger middleware envolvia http.ResponseWriter sin
implementar http.Hijacker ni http.Flusher. Esto rompia el upgrade
WebSocket (501 Not Implemented) y el flush de SSE.

Anade Hijack() y Flush() que delegan al writer subyacente. Detectado
via e2e tests de apps/kanban que arrancaban el binario real y dialeaban
/api/chat/ws — el upgrade fallaba con 501 hasta este fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:00:49 +02:00

56 lines
1.6 KiB
Go

package infra
import (
"bufio"
"fmt"
"io"
"net"
"net/http"
"time"
)
// responseWriter captura el status code escrito al ResponseWriter.
// Implementa http.Hijacker y http.Flusher delegando al writer subyacente
// para preservar WebSocket upgrade y SSE.
type responseWriter struct {
http.ResponseWriter
status int
}
func (rw *responseWriter) WriteHeader(status int) {
rw.status = status
rw.ResponseWriter.WriteHeader(status)
}
// Hijack delega en el ResponseWriter subyacente si lo soporta. Necesario
// para que WebSocket Upgrade funcione a traves del logger middleware.
func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if h, ok := rw.ResponseWriter.(http.Hijacker); ok {
return h.Hijack()
}
return nil, nil, http.ErrNotSupported
}
// Flush delega en el ResponseWriter subyacente si lo soporta. Necesario
// para SSE y respuestas streaming.
func (rw *responseWriter) Flush() {
if f, ok := rw.ResponseWriter.(http.Flusher); ok {
f.Flush()
}
}
// HTTPLoggerMiddleware retorna un Middleware que loguea metodo, path, status y duracion de cada request.
// El formato de cada linea es: METHOD /path STATUS DURACIONms
func HTTPLoggerMiddleware(logger io.Writer) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rw := &responseWriter{ResponseWriter: w, status: http.StatusOK}
next.ServeHTTP(rw, r)
duration := time.Since(start)
fmt.Fprintf(logger, "%s %s %d %dms\n",
r.Method, r.URL.Path, rw.status, duration.Milliseconds())
})
}
}