### Install httpsnoop Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/QUICK-REFERENCE.md Install the httpsnoop library using the go get command. ```bash go get github.com/felixge/httpsnoop ``` -------------------------------- ### Minimal httpsnoop CaptureMetrics Example Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/function-index.md Demonstrates how to use CaptureMetrics to wrap an http.HandlerFunc and log request metrics. This example sets up a basic HTTP server. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func main() { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello")) }) wrapped := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(handler, w, r) log.Printf("status=%d duration=%v bytes=%d", m.Code, m.Duration, m.Written) }) http.ListenAndServe(":8080", wrapped) } ``` -------------------------------- ### Example Usage of CaptureMetrics Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/capture-metrics.md Demonstrates how to wrap an http.Handler with CaptureMetrics to log response metrics. Ensure the httpsnoop package is imported. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func main() { myHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("Hello")) }) wrappedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(myHandler, w, r) log.Printf("code=%d duration=%v bytes=%d", m.Code, m.Duration, m.Written) }) http.ListenAndServe(":8080", wrappedHandler) } ``` -------------------------------- ### Example: Direct Unwrapper Interface Usage Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/unwrap.md Shows how to check if a response writer implements the Unwrapper interface and directly call its Unwrap method. ```go package main import ( "net/http" "github.com/felixge/httpsnoop" ) func main() { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{}) // Check if it implements Unwrapper if u, ok := wrapped.(httpsnoop.Unwrapper); ok { // Get the next layer (typically returns w) nextLayer := u.Unwrap() _ = nextLayer } }) http.ListenAndServe(":8080", h) } ``` -------------------------------- ### Example Usage of CaptureMetricsFn Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/capture-metrics.md Shows how to use CaptureMetricsFn with a callback function to capture metrics when not using a standard http.Handler. The callback receives a wrapped http.ResponseWriter. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func main() { wrapped := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetricsFn(w, func(ww http.ResponseWriter) { ww.WriteHeader(http.StatusCreated) ww.Write([]byte("Created")) }) log.Printf("Response: code=%d bytes=%d", m.Code, m.Written) }) http.ListenAndServe(":8080", wrapped) } ``` -------------------------------- ### Hook Method Binding Example Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/implementation-details.md Example of how hook functions are bound to the state. Each hook receives the next function in the chain and returns a wrapped version. ```go if hooks.Header != nil { state.header = hooks.Header(w.Header) } if hooks.WriteHeader != nil { state.writeHeader = hooks.WriteHeader(w.WriteHeader) } // etc. for other hooks ``` -------------------------------- ### Example Usage of Metrics.CaptureMetrics Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/capture-metrics.md Demonstrates using the Metrics.CaptureMetrics method to update an existing Metrics struct. This is useful for accumulating metrics across different parts of an application. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func main() { wrapped := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.Metrics{Code: http.StatusOK} m.CaptureMetrics(w, func(ww http.ResponseWriter) { ww.Write([]byte("Response body")) }) log.Printf("Captured metrics: Code=%d, Written=%d, Duration=%v", m.Code, m.Written, m.Duration) }) http.ListenAndServe(":8080", wrapped) } ``` -------------------------------- ### Example: Nested Wrappers with Unwrap Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/unwrap.md Illustrates how Unwrap can recursively remove multiple layers of httpsnoop wrappers to reach the original http.ResponseWriter. ```go package main import ( "net/http" "github.com/felixge/httpsnoop" ) func main() { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Create first wrapper layer w1 := httpsnoop.Wrap(w, httpsnoop.Hooks{}) // Create second wrapper layer w2 := httpsnoop.Wrap(w1, httpsnoop.Hooks{}) // Create third wrapper layer w3 := httpsnoop.Wrap(w2, httpsnoop.Hooks{}) // Unwrap recurses through all three layers original := httpsnoop.Unwrap(w3) // original is the same as w }) http.ListenAndServe(":8080", h) } ``` -------------------------------- ### API Reference Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/COMPLETION-SUMMARY.txt Provides full API documentation including exact signatures, parameter types, behavior descriptions, usage examples, performance characteristics, edge case handling, integration patterns, migration guides, architecture explanation, and implementation details. ```APIDOC ## API Reference ### Description This section provides a comprehensive reference to the public API of the httpsnoop package. It includes detailed information on all exported functions, types, and methods. ### Accessing the API Reference To access the full API reference, navigate to the `/api-reference/` folder within the documentation. ### Key Information Provided: - **Exact Signatures and Parameter Types**: Precise details on function and method signatures. - **Complete Descriptions of Behavior**: Explanations of how each API element functions. - **Real-World Usage Examples**: Practical code snippets demonstrating integration. - **Performance Characteristics**: Information on the performance impact of using the package. - **Edge Case Handling**: Documentation on how the package handles various edge cases. - **Integration Patterns**: Guidance on integrating httpsnoop with other systems. - **Migration Guides**: Steps for migrating from older versions or other libraries. - **Architecture Explanation**: Overview of the package's design and architecture. - **Implementation Details**: In-depth look at the package's internal workings. ``` -------------------------------- ### Generated Wrapper Type Examples Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/implementation-details.md Examples of generated wrapper types, showing how they are named based on the combination of optional interfaces they implement. The base case 'rw0' implements no optional interfaces. ```go type rw0 rwState // No optional interfaces (base case) type rw1 rwState // io.StringWriter only type rw2 rwState // http.Pusher only // ... up to rw511 ``` -------------------------------- ### Example: Type Assertion with Unwrap Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/unwrap.md Demonstrates using Unwrap to access the underlying http.ResponseWriter and perform type assertions for additional interfaces like http.Hijacker. ```go package main import ( "bufio" "net" "net/http" "github.com/felixge/httpsnoop" ) func main() { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { return next(p) } }, }) // Unwrap to access the underlying writer underlying := httpsnoop.Unwrap(wrapped) // Now we can type-assert to other interfaces if hijacker, ok := underlying.(http.Hijacker); ok { conn, rw, err := hijacker.Hijack() if err == nil { // Handle hijacked connection conn.Close() } } }) http.ListenAndServe(":8080", h) } ``` -------------------------------- ### Panic Recovery in Write Hook Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/usage-patterns.md Illustrates how hooks can recover from panics occurring in the underlying methods. This example uses a defer/recover mechanism within the Write hook to log panics gracefully. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func panicRecoveryHook(w http.ResponseWriter, r *http.Request) { wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { defer func() { if err := recover(); err != nil { log.Printf("Write panic: %v", err) } }() return next(p) } }, }) wrapped.Write([]byte("Response")) } ``` -------------------------------- ### Inline Hook Call Example Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/implementation-details.md Illustrates how generated method calls on wrapper types, like WriteHeader, are often inlined by the Go compiler for performance. ```go func (w *rw0) WriteHeader(code int) { w.writeHeader(code) // Often inlined } ``` -------------------------------- ### CaptureMetrics Hook Implementation Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/architecture-and-design.md Demonstrates how to implement custom hooks for metric collection, specifically for tracking HTTP status codes and bytes written. This example shows how to define WriteHeader and Write hooks. ```go Hooks{ WriteHeader: func(next WriteHeaderFunc) WriteHeaderFunc { return func(code int) { next(code) if !(code >= 100 && code <= 199) && !headerWritten { m.Code = code headerWritten = true } } }, Write: func(next WriteFunc) WriteFunc { return func(p []byte) (int, error) { n, err := next(p) m.Written += int64(n) headerWritten = true return n, err } }, // ... other hooks ... } ``` -------------------------------- ### Hook Error Handling in Write Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/usage-patterns.md Demonstrates how hooks can return errors, affecting the handler's execution flow. This example simulates a write failure and shows how the handler can react by setting an internal server error status. ```go package main import ( "io" "net/http" "github.com/felixge/httpsnoop" ) func errorHandlingHook(w http.ResponseWriter, r *http.Request) { wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { // Simulate a write failure if len(p) > 1000 { return 0, io.ErrShortWrite } return next(p) } }, }) // Handler can check error from Write if n, err := wrapped.Write([]byte("Response")); err != nil { wrapped.WriteHeader(http.StatusInternalServerError) } } ``` -------------------------------- ### Example Combination ID Calculation Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/architecture-and-design.md Illustrates how to calculate the combination ID for a ResponseWriter implementing http.Flusher, http.CloseNotifier, and io.ReaderFrom. The resulting ID (336) is used to select the correct wrapper type. ```Go If a ResponseWriter implements: - http.Flusher (bit 8) - http.CloseNotifier (bit 6) - io.ReaderFrom (bit 4) Then combination ID = (1<<8) | (1<<6) | (1<<4) = 256 + 64 + 16 = 336 The Wrap function returns (*rw336)(state). ``` -------------------------------- ### Wrap HTTP Response Writer with a Basic Write Hook Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/wrap.md This example demonstrates how to wrap an http.ResponseWriter to intercept and log the number of bytes written. It uses a Write hook to log before calling the original Write function. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func main() { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { log.Printf("Writing %d bytes", len(p)) return next(p) } }, }) wrapped.Write([]byte("Hello")) }) http.ListenAndServe(":8080", h) } ``` -------------------------------- ### Build Custom Middleware with Response Visibility Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/README.md Create custom middleware that gains visibility into response details like the status code. This example shows how to check for unauthorized requests based on the captured metrics. ```go func authMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(next, w, r) if m.Code == http.StatusUnauthorized { // Log unauthorized attempts } }) } ``` -------------------------------- ### Wrap HTTP Response Writer with WriteHeader and Write Hooks Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/wrap.md This example shows how to use multiple hooks, WriteHeader and Write, to intercept both the status code and the response body. It logs the header code and the number of bytes written before proceeding with the original methods. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func main() { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{ WriteHeader: func(next httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc { return func(code int) { log.Printf("Writing header: %d", code) next(code) } }, Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { log.Printf("Writing %d bytes", len(p)) return next(p) } }, }) wrapped.WriteHeader(http.StatusCreated) wrapped.Write([]byte("Created")) }) http.ListenAndServe(":8080", h) } ``` -------------------------------- ### CaptureMetrics Functions and Method Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/FILE-MANIFEST.md Documentation for the CaptureMetrics function, CaptureMetricsFn function, and the Metrics.CaptureMetrics method. This section details their signatures, parameters, return values, and provides usage examples for capturing metrics. ```APIDOC ## CaptureMetrics Functions and Method ### Description Provides functions and a method for capturing metrics related to HTTP requests and responses. ### Functions/Methods - **CaptureMetrics()** - Signature, parameters, returns, and examples. - **CaptureMetricsFn()** - Signature, parameters, returns, and examples. - **Metrics.CaptureMetrics()** - Signature, parameters, returns, and examples. ### Type Definition - **Metrics** - Field descriptions for the Metrics type. ``` -------------------------------- ### Wrap Function and Hooks Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/FILE-MANIFEST.md Comprehensive reference for the Wrap function and its associated Hooks. This covers the function's full documentation, examples, hook precedence, and detailed explanations of all 13 hook fields and function types. ```APIDOC ## Wrap Function and Hooks ### Description Allows wrapping an http.ResponseWriter to intercept and modify request/response behavior using hooks. ### Function - **Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter** - Full documentation, examples, and hook precedence. ### Types - **Hooks** - Documentation for all 13 hook fields (e.g., WriteFunc, FlushFunc, PushFunc). - **Hook Function Types** - Documentation for all individual hook function types. ``` -------------------------------- ### Custom HTTP Hook Implementation Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/wrap.md Implements custom hooks for WriteHeader and Write to log status codes and bytes written. This example shows how to define and use custom hooks with the httpsnoop.Wrap function. ```Go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func loggingHook() httpsnoop.Hooks { return httpsnoop.Hooks{ WriteHeader: func(next httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc { return func(code int) { log.Printf("Status: %d", code) next(code) } }, Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { n, err := next(p) log.Printf("Wrote %d bytes", n) return n, err } }, } } func main() { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wrapped := httpsnoop.Wrap(w, loggingHook()) wrapped.WriteHeader(http.StatusOK) wrapped.Write([]byte("Hello")) }) http.ListenAndServe(":8080", h) } ``` -------------------------------- ### Distributed Tracing Integration Middleware Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/usage-patterns.md This middleware integrates with OpenTelemetry to add metrics to distributed traces. It starts a span for each request and sets attributes based on captured metrics. ```go package main import ( "net/http" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" "github.com/felixge/httpsnoop" ) func tracingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, span := otel.Tracer("http").Start(r.Context(), r.URL.Path) defer span.End() m := httpsnoop.CaptureMetrics(next, w, r.WithContext(ctx)) span.SetAttributes( attribute.Int("http.status_code", m.Code), attribute.Int64("http.response_body_size", m.Written), attribute.String("http.duration_ms", fmt.Sprint(m.Duration.Milliseconds())), ) }) } ``` -------------------------------- ### Implement Custom Behavior with Hooks Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/README.md This example shows how to use httpsnoop's hook system to intercept specific methods of the http.ResponseWriter. Here, a hook is added to log the number of bytes being written before the original Write method is called. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func main() { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { log.Printf("Writing %d bytes", len(p)) return next(p) } }, }) wrapped.Write([]byte("Hello")) h}) http.ListenAndServe(":8080", h) } ``` -------------------------------- ### Chaining Middleware with Metrics Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Demonstrates the correct way to chain middleware, including a metrics middleware, to ensure proper capture and logging of response data. ```go func metricsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(next, w, r) log.Printf("status=%d duration=%v bytes=%d", m.Code, m.Duration, m.Written) }) } func main() { handler := myHandler handler = metricsMiddleware(handler) // Outer: captures metrics handler = authMiddleware(handler) // Middle: auth checks handler = corsMiddleware(handler) # Inner: CORS setup http.ListenAndServe(":8080", handler) } ``` -------------------------------- ### Integrating Middleware with gorilla/mux Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Demonstrates how to apply middleware to a gorilla/mux router, either globally or by wrapping the router. ```go import "github.com/gorilla/mux" router := mux.NewRouter() // Option 1: Use middleware router.Use(metricsMiddleware) // Option 2: Wrap router http.ListenAndServe(":8080", metricsMiddleware(router)) ``` -------------------------------- ### Testing Handlers with httptest and httpsnoop (After Migration) Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Shows how to test HTTP handlers using Go's httptest.NewRecorder and httpsnoop.CaptureMetricsFn for simplified metric capture. ```go func TestHandler(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) w := httptest.NewRecorder() m := httpsnoop.CaptureMetricsFn(w, func(ww http.ResponseWriter) { handler(ww, req) }) if m.Code != 200 { t.Fatalf("code = %d, want 200", m.Code) } if m.Written == 0 { t.Fatal("no bytes written") } } ``` -------------------------------- ### Deprecated CloseNotifier Hook Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Example of a deprecated hook with a comment indicating its status and suggesting an alternative. This is a hypothetical scenario for demonstration. ```go // Deprecated: Use context.Context from Request instead CloseNotify func(CloseNotifyFunc) CloseNotifyFunc ``` -------------------------------- ### Log slow requests middleware Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/QUICK-REFERENCE.md A middleware function that logs details for HTTP requests exceeding a specified duration threshold (100ms in this example). ```go func slowRequestMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(next, w, r) if m.Duration > 100*time.Millisecond { log.Printf("SLOW: %s %s took %dms", r.Method, r.URL.Path, m.Duration.Milliseconds()) } }) } ``` -------------------------------- ### Chain Middleware Functions Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/QUICK-REFERENCE.md Demonstrates how to chain multiple middleware functions before passing the final handler to http.ListenAndServe. Order matters for execution. ```go handler := myHandler handler = metricsMiddleware(handler) // Outer handler = authMiddleware(handler) // Middle handler = corsMiddleware(handler) // Inner http.ListenAndServe(":8080", handler) ``` -------------------------------- ### Simple Wrapper Pattern Migration Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Migrate from a basic custom ResponseWriter wrapper to httpsnoop's CaptureMetrics for comprehensive metrics. ```go type loggingWriter struct { http.ResponseWriter statusCode int } func (w *loggingWriter) WriteHeader(code int) { w.statusCode = code w.ResponseWriter.WriteHeader(code) } func (w *loggingWriter) Write(p []byte) (int, error) { return w.ResponseWriter.Write(p) } ``` ```go import "github.com/felixge/httpsnoop" func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(next, w, r) // Use m.Code, m.Duration, m.Written }) } ``` -------------------------------- ### Applying Multiple Hooks Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/architecture-and-design.md Demonstrates how to chain multiple hook functions together to sequentially modify the behavior of a method. This allows for a flexible and composable way to add cross-cutting concerns. ```Go Wrap(w, Hooks{ Write: hook1(hook2(hook3(w.Write))), }) ``` -------------------------------- ### Integrate Middleware with standard net/http Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/QUICK-REFERENCE.md Apply middleware to a standard net/http ServeMux by wrapping the mux with the middleware function before passing it to http.ListenAndServe. ```go mux := http.NewServeMux() handler := metricsMiddleware(mux) http.ListenAndServe(":8080", handler) ``` -------------------------------- ### Testing Handlers with Mock ResponseWriter (Before httpsnoop) Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Demonstrates a traditional approach to testing HTTP handlers using a custom mock ResponseWriter in Go. ```go type mockWriter struct { status int data bytes.Buffer } func (m *mockWriter) Write(p []byte) (int, error) { return m.data.Write(p) } func (m *mockWriter) WriteHeader(code int) { m.status = code } func TestHandler(t *testing.T) { w := &mockWriter{} req := httptest.NewRequest("GET", "/", nil) handler(w, req) if w.status != 200 { t.Fatalf("status = %d, want 200", w.status) } } ``` -------------------------------- ### Integrating Middleware with chi Router Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Illustrates applying middleware to a chi router, either globally or for specific routes. ```go import "github.com/go-chi/chi/v5" router := chi.NewRouter() // Option 1: Global middleware router.Use(metricsMiddleware) // Option 2: Specific route router.Get("/api", metricsMiddleware(http.HandlerFunc(apiHandler)).ServeHTTP) ``` -------------------------------- ### Unwrap Function Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/FILE-MANIFEST.md Reference for the Unwrap function, including its signature, parameters, and return values. This section also covers the Unwrapper interface definition and provides use cases and examples for unwrapping nested layers of ResponseWriter. ```APIDOC ## Unwrap Function ### Description Retrieves the underlying http.ResponseWriter from a wrapped writer. ### Function - **Unwrap(w http.ResponseWriter) http.ResponseWriter** - Signature, parameters, and returns. ### Interface - **Unwrapper** - Definition of the Unwrapper interface. ### Use Cases - Accessing the original ResponseWriter. - Type assertion after unwrapping. ``` -------------------------------- ### Upgrading Code with Optional New Hook Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Illustrates how existing Go code remains compatible while optionally incorporating a new hook. ```go // Old code still works - new hook is optional w := httpsnoop.Wrap(writer, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { // ... }, }) ``` ```go // New code adds the new hook w := httpsnoop.Wrap(writer, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { // ... }, NewMethod: func(next httpsnoop.NewMethodFunc) httpsnoop.NewMethodFunc { return func(...) (...) { // Handle new method return next(...) } }, }) ``` -------------------------------- ### HTTP Handler Metrics Benchmarks Source: https://github.com/felixge/httpsnoop/blob/master/README.md Performance benchmarks comparing a baseline http.Handler with one instrumented using httpsnoop.CaptureMetrics. The results indicate a negligible overhead introduced by the package. ```go BenchmarkBaseline-8 20000 94912 ns/op BenchmarkCaptureMetrics-8 20000 95461 ns/op ``` -------------------------------- ### Regenerate Code Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/implementation-details.md Run this command to regenerate Go code, specifically rebuilding wrap_generated.go from the codegen tool. ```bash cd httpsnoop go generate ./... ``` -------------------------------- ### Middleware hook pattern Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/QUICK-REFERENCE.md Illustrates the general pattern for creating hooks for httpsnoop wrappers, following a middleware-like structure. ```go Hook: func(original) original { return func(args) returns { // Pre-call behavior result := original(args) // Post-call behavior return result } } ``` -------------------------------- ### Integrating httpsnoop with echo Framework Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Shows how to use httpsnoop within the echo framework's middleware system to capture metrics. ```go import "github.com/labstack/echo/v4" e := echo.New() // Use echo middleware that wraps with httpsnoop e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { m := httpsnoop.CaptureMetricsFn(c.Response(), func(w http.ResponseWriter) { c.Response().Writer = w next(c) }) log.Printf("status=%d duration=%v", m.Code, m.Duration) return nil } }) ``` -------------------------------- ### Capturing Metrics with httptest Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Transition from httptest.ResponseRecorder to httpsnoop.CaptureMetricsFn for comprehensive metrics when testing HTTP handlers. ```go // Before: Limited metrics from httptest.ResponseRecorder recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, req) // recorder.Code, recorder.Body available but no timing ``` ```go // After: Full metrics with httpsnoop recorder := httptest.NewRecorder() m := httpsnoop.CaptureMetricsFn(recorder, func(w http.ResponseWriter) { handler.ServeHTTP(w, req) }) // m.Code, m.Duration, m.Written all available ``` -------------------------------- ### Panic Recovery for Metric Collection Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/architecture-and-design.md Shows how to use `defer` with `recover` to ensure that metrics, such as request duration, are recorded even if the wrapped handler function panics. This enhances the robustness of metric collection. ```Go defer func() { m.Duration += time.Since(start) }() fn(Wrap(w, hooks)) ``` -------------------------------- ### Capture HTTP Metrics with Basic Usage Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/README.md This snippet demonstrates the most common use case: wrapping an existing http.Handler to capture metrics. It logs the HTTP status code, duration, and bytes written after the handler has executed. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func main() { myHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello")) }) wrappedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(myHandler, w, r) log.Printf("code=%d duration=%v bytes=%d", m.Code, m.Duration, m.Written) }) http.ListenAndServe(":8080", wrappedHandler) } ``` -------------------------------- ### Duplex Hooks (Go 1.21+) Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/function-index.md Middleware for EnableFullDuplex calls. ```APIDOC ## EnableFullDuplexFunc ### Description Middleware for `EnableFullDuplex()` calls (for bidirectional communication). ### Type Definition ```go type EnableFullDuplexFunc func() error ``` ``` -------------------------------- ### Wrapping net/http Mux with Middleware Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Shows two methods for integrating middleware with net/http Mux in Go: wrapping the entire mux or individual handlers. ```go mux := http.NewServeMux() // Option 1: Wrap entire mux handler := metricsMiddleware(mux) http.ListenAndServe(":8080", handler) // Option 2: Wrap individual handlers mux.HandleFunc("/api", metricsMiddleware(http.HandlerFunc(apiHandler)).ServeHTTP) ``` -------------------------------- ### HTTP/2 Hooks Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/function-index.md Middleware for Push(target string, opts *http.PushOptions) calls. ```APIDOC ## PushFunc ### Description Middleware for `Push(target string, opts *http.PushOptions)` calls (http.Pusher). ### Type Definition ```go type PushFunc func(target string, opts *http.PushOptions) error ``` ``` -------------------------------- ### Pointer Receiver for Interface Methods Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/implementation-details.md Demonstrates the use of a pointer receiver for methods on a wrapper type to ensure the correct method set for interface implementation. ```go type rwN rwState func (w *rwN) Header() http.Header { return w.doHeader() } ``` -------------------------------- ### Deadline Hooks (Go 1.20+) Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/function-index.md Middleware for SetReadDeadline and SetWriteDeadline calls. ```APIDOC ## SetReadDeadlineFunc ### Description Middleware for `SetReadDeadline(deadline time.Time)` calls. ### Type Definition ```go type SetReadDeadlineFunc func(deadline time.Time) error ``` ``` ```APIDOC ## SetWriteDeadlineFunc ### Description Middleware for `SetWriteDeadline(deadline time.Time)` calls. ### Type Definition ```go type SetWriteDeadlineFunc func(deadline time.Time) error ``` ``` -------------------------------- ### Adding Metrics to Existing Middleware Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Integrate httpsnoop.CaptureMetrics into existing middleware to gain access to response metrics within cleanup logic. ```go func existingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Some setup defer func() { // Some cleanup }() next.ServeHTTP(w, r) }) } ``` ```go import "github.com/felixge/httpsnoop" func enhancedMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Some setup m := httpsnoop.CaptureMetrics(next, w, r) defer func() { // Some cleanup, now with access to m.Code, m.Duration, m.Written }() }) } ``` -------------------------------- ### Simple Request Logging Middleware Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/usage-patterns.md This middleware captures request metrics like status code, duration, and bytes written, then logs them. It's useful for basic request instrumentation. ```go package main import ( "log" "net/http" "time" "github.com/felixge/httpsnoop" ) func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(next, w, r) log.Printf("%s %s - status=%d duration=%dms bytes=%d", r.Method, r.URL.Path, m.Code, m.Duration.Milliseconds(), m.Written) }) } func main() { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello")) }) http.ListenAndServe(":8080", loggingMiddleware(handler)) } ``` -------------------------------- ### Chained Middleware with Multiple Wraps Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/usage-patterns.md Illustrates stacking multiple httpsnoop wrapper layers to handle different aspects of request/response processing. Each wrap can intercept specific events like Write or WriteHeader. ```go package main import ( "net/http" "github.com/felixge/httpsnoop" ) func chainedWrapHandler(w http.ResponseWriter, r *http.Request) { // First wrap: log writes w1 := httpsnoop.Wrap(w, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { println("Write called") return next(p) } }, }) // Second wrap: log headers w2 := httpsnoop.Wrap(w1, httpsnoop.Hooks{ WriteHeader: func(next httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc { return func(code int) { println("WriteHeader called") next(code) } }, }) w2.WriteHeader(http.StatusOK) w2.Write([]byte("Response")) // Unwrap both layers original := httpsnoop.Unwrap(w2) // original is w } ``` -------------------------------- ### Integrate Middleware with gorilla/mux Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/QUICK-REFERENCE.md Add middleware to a gorilla/mux router using the Use method. This applies the middleware to all routes defined on the router. ```go router := mux.NewRouter() router.Use(metricsMiddleware) ``` -------------------------------- ### Custom Response Writer Unwrapping Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/usage-patterns.md Demonstrates how to unwrap a response writer to access its underlying interfaces, such as http.Hijacker. This is useful for advanced operations like hijacking the connection. ```go package main import ( "bufio" "log" "net" "net/http" "github.com/felixge/httpsnoop" ) func unwrapHandler(w http.ResponseWriter, r *http.Request) { // Wrap the writer with some hooks wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{}) // Unwrap to get the original original := httpsnoop.Unwrap(wrapped) // Try to hijack the connection if hijacker, ok := original.(http.Hijacker); ok { conn, rw, err := hijacker.Hijack() if err == nil { defer conn.Close() // Handle the hijacked connection for WebSocket, etc. rw.WriteString("HTTP/1.1 101 Switching Protocols\r\n\r\n") rw.Flush() } } } ``` -------------------------------- ### Project File Structure Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/00-START-HERE.md This code block displays the directory structure of the httpsnoop project, showing the location of various documentation files and API reference directories. ```text /workspace/home/output/ ├── 00-START-HERE.md ← You are here ├── QUICK-REFERENCE.md (copy-paste examples) ├── DOCUMENTATION-INDEX.md (full index) ├── README.md (overview) ├── types.md (type reference) ├── usage-patterns.md (10+ examples) ├── architecture-and-design.md (design explanation) ├── implementation-details.md (internals) ├── migration-and-compatibility.md (upgrading) └── api-reference/ ├── function-index.md (all functions) ├── capture-metrics.md (CaptureMetrics API) ├── wrap.md (Wrap & Hooks API) └── unwrap.md (Unwrap API) ``` -------------------------------- ### Use httpsnoop as middleware Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/QUICK-REFERENCE.md Create a middleware function that wraps the next http.Handler to capture metrics for each request. ```go func metricsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(next, w, r) log.Printf("%s %s - %d in %v", r.Method, r.URL, m.Code, m.Duration) }) } // Usage handler := metricsMiddleware(myHandler) http.ListenAndServe(":8080", handler) ``` -------------------------------- ### Migrating OpenTelemetry Instrumentation to httpsnoop Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Shows how to integrate httpsnoop metrics into OpenTelemetry tracing, enhancing span attributes with HTTP status and response size. ```go // Before: Manual span setup func traceMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, span := tracer.Start(r.Context(), r.URL.Path) defer span.End() next.ServeHTTP(w, r.WithContext(ctx)) }) } // After: With httpsnoop metrics func traceMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, span := tracer.Start(r.Context(), r.URL.Path) defer span.End() m := httpsnoop.CaptureMetrics(next, w, r.WithContext(ctx)) span.SetAttributes( attribute.Int("http.status", m.Code), attribute.Int64("http.response_bytes", m.Written), ) }) } ``` -------------------------------- ### Migrating Prometheus Instrumentation to httpsnoop Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Compares manual Prometheus instrumentation with using httpsnoop for capturing metrics like duration and response size. ```go // Before: Manual instrumentation func handler(w http.ResponseWriter, r *http.Request) { start := time.Now() w.Write([]byte("response")) // Manual duration tracking } // After: httpsnoop-based func handler(w http.ResponseWriter, r *http.Request) { wrapped := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("response")) }) m := httpsnoop.CaptureMetrics(wrapped, w, r) requestDuration.Observe(m.Duration.Seconds()) responseSize.Observe(float64(m.Written)) } ``` -------------------------------- ### Detecting New Interface with Type Assertion Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Demonstrates how to detect a new interface type using type assertion in Go. ```go if t9, i9 := w.(newInterface); i9 { combo |= 1 << 9 // Handle the new interface } ``` -------------------------------- ### Request and Response Body Logging Handler Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/usage-patterns.md This handler demonstrates logging request and response bodies using hooks. It's primarily for debugging purposes and should be used with caution due to potential performance impacts and sensitive data exposure. ```go package main import ( "bytes" "io" "log" "net/http" "github.com/felixge/httpsnoop" ) func bodyLoggingHandler(w http.ResponseWriter, r *http.Request) { // Log request body if r.Body != nil { body, _ := io.ReadAll(r.Body) log.Printf("Request body: %s", string(body)) r.Body = io.NopCloser(bytes.NewReader(body)) } // Wrap writer to log response body wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { log.Printf("Response body: %s", string(p)) return next(p) } }, }) // Handler code here writes to wrapped wrapped.Write([]byte("Response")) } ``` -------------------------------- ### Custom Interface-Preserving Wrapper Migration Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/migration-and-compatibility.md Replace fragile custom wrappers that attempt to preserve interfaces with httpsnoop.Wrap for robust interface handling. ```go type wrappedWriter struct { http.ResponseWriter http.Flusher http.Hijacker io.ReaderFrom io.StringWriter // ... more interfaces ... } ``` ```go wrapped := httpsnoop.Wrap(w, httpsnoop.Hooks{ Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { return func(p []byte) (int, error) { // Custom behavior return next(p) } }, }) ``` -------------------------------- ### Metrics Object Reuse with Custom Initial State Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/usage-patterns.md Shows how to reuse a Metrics object and customize its initial state using CaptureMetrics. This allows setting default values like http.StatusServiceUnavailable before the handler executes. ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func metricsReuseHandler(w http.ResponseWriter, r *http.Request) { // Create metrics with custom initial state m := httpsnoop.Metrics{ Code: http.StatusServiceUnavailable, // Default to unavailable } m.CaptureMetrics(w, func(ww http.ResponseWriter) { // If handler doesn't write a status, it will default to ServiceUnavailable ww.Write([]byte("Response")) }) log.Printf("Final code: %d", m.Code) } ``` -------------------------------- ### Generated Wrapper Type Structure Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/architecture-and-design.md Illustrates the typical structure of a generated wrapper type `rwN`. It embeds `rwState`, implements the `Unwrapper` interface, and provides methods that delegate to hooked functions for all exposed interfaces. ```Go type rwN rwState func (w *rwN) Unwrap() http.ResponseWriter { return w.w } // Methods for interface N: func (w *rwN) Header() http.Header { /* call w.header via hooks */ } func (w *rwN) WriteHeader(code int) { /* call w.writeHeader via hooks */ } func (w *rwN) Write(b []byte) (int, error) { /* call w.write via hooks */ } // Optional methods (only if interface N includes them): func (w *rwN) Flush() { /* call w.flush via hooks */ } func (w *rwN) CloseNotify() <-chan bool { /* ... */ } // ... other interfaces ... ``` -------------------------------- ### Integrate Middleware with chi Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/QUICK-REFERENCE.md Add middleware to a chi router using the Use method. This applies the middleware to all routes defined on the router. ```go router := chi.NewRouter() router.Use(metricsMiddleware) ``` -------------------------------- ### Capture HTTP Metrics with httpsnoop Source: https://github.com/felixge/httpsnoop/blob/master/README.md This snippet demonstrates how to wrap an existing http.Handler with httpsnoop.CaptureMetrics to log request details. It logs the HTTP method, URL, status code, duration, and bytes written for each incoming request. ```go import ( "log" "net/http" "github.com/felixge/httpsnoop" ) // myH is your app's http handler, perhaps a http.ServeMux or similar. var myH http.Handler // wrappedH wraps myH in order to log every request. wrappedH := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(myH, w, r) log.Printf( "%s %s (code=%d dt=%s written=%d)", r.Method, r.URL, m.Code, m.Duration, m.Written, ) }) http.ListenAndServe(":8080", wrappedH) ``` -------------------------------- ### Metric Capture for ReadFrom Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/implementation-details.md Implementation of metric capture for ReadFrom. It accumulates bytes read after the call and sets headerWritten to true. ```go ReadFrom: func(next ReadFromFunc) ReadFromFunc { return func(src io.Reader) (int64, error) { n, err := next(src) headerWritten = true m.Written += n return n, err } }, ``` -------------------------------- ### Import Statement for httpsnoop Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/DOCUMENTATION-INDEX.md Use this import statement to include the httpsnoop package in your Go projects. ```go import "github.com/felixge/httpsnoop" ``` -------------------------------- ### Metric Capture for Write Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/implementation-details.md Implementation of metric capture for the Write method. It accumulates bytes written after the call and sets headerWritten to true. ```go Write: func(next WriteFunc) WriteFunc { return func(p []byte) (int, error) { n, err := next(p) m.Written += int64(n) headerWritten = true return n, err } }, ``` -------------------------------- ### CaptureMetricsFn API function Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/QUICK-REFERENCE.md The CaptureMetricsFn function wraps a ResponseWriter, executes a callback function, and returns the captured metrics. ```go m := httpsnoop.CaptureMetricsFn(w, func(ww http.ResponseWriter) { ww.Write([]byte("response")) }) ``` -------------------------------- ### Collecting Statistics from Multiple Handlers Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/usage-patterns.md Aggregate metrics such as total requests, bytes written, duration, and error count from different HTTP handlers using a middleware pattern. ```go package main import ( "log" "net/http" "sync" "github.com/felixge/httpsnoop" ) type Stats struct { mu sync.Mutex totalRequests int totalBytes int64 totalDuration float64 errorCount int } func (s *Stats) Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(next, w, r) s.mu.Lock() defer s.mu.Unlock() s.totalRequests++ s.totalBytes += m.Written s.totalDuration += m.Duration.Seconds() if m.Code >= 400 { s.errorCount++ } }) } func (s *Stats) Report() { s.mu.Lock() defer s.mu.Unlock() avgBytes := float64(s.totalBytes) / float64(s.totalRequests) avgDuration := s.totalDuration / float64(s.totalRequests) log.Printf("Stats: requests=%d, avg_bytes=%.0f, avg_duration=%.4fs, errors=%d", s.totalRequests, avgBytes, avgDuration, s.errorCount) } func main() { stats := &Stats{} handler := stats.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello")) })) // ... start server ... } ``` -------------------------------- ### CaptureMetricsFn Source: https://github.com/felixge/httpsnoop/blob/master/_autodocs/api-reference/capture-metrics.md Wraps a response writer and executes a callback function, returning metrics captured from the execution. This is a lower-level API that accepts a callback function instead of an http.Handler. ```APIDOC ## CaptureMetricsFn ### Description Wraps a response writer and executes a callback function, returning metrics captured from the execution. ### Method Not applicable (Go function) ### Signature func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metrics ### Parameters #### Function Parameters - **w** (http.ResponseWriter) - The response writer to wrap - **fn** (func(http.ResponseWriter)) - Callback function that receives the wrapped response writer and performs the HTTP operation ### Return Type `Metrics` — A struct containing the captured response code, duration, and bytes written. ### Example ```go package main import ( "log" "net/http" "github.com/felixge/httpsnoop" ) func main() { wrapped := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetricsFn(w, func(ww http.ResponseWriter) { ww.WriteHeader(http.StatusCreated) ww.Write([]byte("Created")) }) log.Printf("Response: code=%d bytes=%d", m.Code, m.Written) }) http.ListenAndServe(":8080", wrapped) } ``` ```