From db7af1a6b7c0e04a9db225804d9c823bc898b59f Mon Sep 17 00:00:00 2001 From: Nathan Lubchenco Date: Thu, 12 Oct 2023 08:41:52 -0600 Subject: [PATCH] first pass at adding a with formatting option, WIP - not working --- httpevents/handler.go | 96 +++++++++++++++++++++++++++++++++++++- httpevents/handler_test.go | 49 +++++++++++++++++++ 2 files changed, 143 insertions(+), 2 deletions(-) diff --git a/httpevents/handler.go b/httpevents/handler.go index 3497e5b..8f8df0a 100644 --- a/httpevents/handler.go +++ b/httpevents/handler.go @@ -2,6 +2,7 @@ package httpevents import ( "bufio" + "fmt" "net" "net/http" "sync" @@ -68,11 +69,83 @@ func NewHandlerWith(logger *events.Logger, handler http.Handler) http.Handler { }) } +type LoggerFunc func(headers http.Header) http.Header + +func copyHeaders(headers http.Header) http.Header { + fmt.Println("Copying headers") + fmt.Println("Existing Header") + for k, v := range headers { + fmt.Println(k) + fmt.Println(v) + } + headersCopy := make(http.Header) + + for k, v := range headers { + headersCopy[k] = v + } + + fmt.Println("New Headers") + for k, v := range headersCopy { + fmt.Println(k) + fmt.Println(v) + } + + return headersCopy +} +func NewHandlerWithFormatting(formatter LoggerFunc, logger *events.Logger, handler http.Handler) http.Handler { + return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + var laddr string + + if value, ok := req.Context().Value(http.LocalAddrContextKey).(net.Addr); ok { + laddr = value.String() + } + + w := responseWriterPool.Get().(*responseWriter) + // We capture all the values we need from req in case the object + // gets modified by the handler. + w.ResponseWriter = res + w.logger = logger + w.SanitizeHeaders = formatter + w.request.reset(req, laddr) + + // If the handler panics we want to make sure we report the issue in the + // access log, while also ensuring that a response is going to be sent + // down to the client. + // We don't silence the panic here tho and instead we forward it back to + // the parent handler which may need to be aware that a panic occurred. + defer func() { + err := recover() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } + + w.ResponseWriter = nil + w.logger = nil + w.wroteHeader = false + w.request.release() + responseWriterPool.Put(w) + + if err != nil { + panic(err) + } + }() + + // The request is forwarded to the handler, if it never calls the + // writer's WriteHeader method we force the call with "200 OK" status + // to match the default behavior of the net/http package (and also make + // sure an access log will be written). + handler.ServeHTTP(w, req) + w.WriteHeader(http.StatusOK) + }) + +} + type responseWriter struct { http.ResponseWriter logger *events.Logger request - wroteHeader bool + wroteHeader bool + SanitizeHeaders LoggerFunc } func (w *responseWriter) WriteHeader(status int) { @@ -103,7 +176,26 @@ func (w *responseWriter) log(depth int, status int) { w.logger = nil w.request.status = status w.request.statusText = http.StatusText(status) - w.request.log(logger, w.ResponseWriter.Header(), depth+1) + + if w.SanitizeHeaders != nil { + fmt.Println("Sanitizing headers") + for k, v := range w.ResponseWriter.Header() { + fmt.Println(k) + fmt.Println(v) + } + headers := w.SanitizeHeaders(copyHeaders(w.ResponseWriter.Header())) + fmt.Println("Headers") + println(w.ResponseWriter.Header()) + + println(w.ResponseWriter.Header().Get("User-Agent")) + fmt.Println("Changed Headers") + println(headers) + println(headers.Get("User-Agent")) + w.request.log(logger, headers, depth+1) + } else { + fmt.Println("MISSING FUNC") + w.request.log(logger, w.ResponseWriter.Header(), depth+1) + } } } diff --git a/httpevents/handler_test.go b/httpevents/handler_test.go index e5f8b04..04dc636 100644 --- a/httpevents/handler_test.go +++ b/httpevents/handler_test.go @@ -2,6 +2,7 @@ package httpevents import ( "context" + "fmt" "net/http" "net/http/httptest" "testing" @@ -79,6 +80,54 @@ func TestHandler(t *testing.T) { }) } +func filterHeaders(headers http.Header) http.Header { + fmt.Println("before") + fmt.Println(headers) + headers.Del("User-Agent") + fmt.Println("after") + fmt.Println(headers) + return headers +} +func TestNewHandlerWithFormatting(t *testing.T) { + eventsHandler := &eventstest.Handler{} + + req := httptest.NewRequest("GET", "/hello?answer=42", nil) + req.Header.Set("User-Agent", "httpevents") + req.Header.Set("Authorization", "this will be deleted") + req.URL.Fragment = "universe" // for some reason NewRequest doesn't parses this + req.Host = "www.github.com" + req.RemoteAddr = "127.0.0.1:56789" + req = req.WithContext(context.WithValue(req.Context(), http.LocalAddrContextKey, mockAddr{ + s: "127.0.0.1:80", + n: "tcp", + })) + + res := httptest.NewRecorder() + log := events.NewLogger(eventsHandler) + + h := NewHandlerWithFormatting(filterHeaders, log, http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(http.StatusAccepted) + })) + h.ServeHTTP(res, req) + + eventsHandler.AssertEvents(t, events.Event{ + Message: `127.0.0.1:80->127.0.0.1:56789 - www.github.com - GET /hello?answer=42#universe - 202 Accepted - "httpevents"`, + Args: events.Args{ + {Name: "local_address", Value: "127.0.0.1:80"}, + {Name: "remote_address", Value: "127.0.0.1:56789"}, + {Name: "host", Value: "www.github.com"}, + {Name: "method", Value: "GET"}, + //{Name: "path", Value: "/hello"}, + {Name: "query", Value: "answer=42"}, + {Name: "fragment", Value: "universe"}, + {Name: "status", Value: 202}, + {Name: "request", Value: &headerList{{name: "User-Agent", value: "httpevents"}}}, + {Name: "response", Value: &headerList{}}, + }, + Debug: true, + }) +} + func TestHandlerPanic(t *testing.T) { eventsHandler := &eventstest.Handler{}