Documentation
¶
Overview ¶
Package httpw provides a single canonical http.ResponseWriter proxy shared by every middleware that needs to observe a response.
Middlewares routinely wrap the ResponseWriter to capture the status code and byte count, or to tee the body for logging. Hand-rolling that wrapper in each middleware is both duplication and a trap: a naive wrapper silently hides the underlying writer's optional interfaces (http.Flusher, http.Hijacker, …), so an interposed middleware breaks SSE/streaming Flush and WebSocket Hijack.
Writer sidesteps that by staying a single type and exposing the wrapped writer via Unwrap. Callers reach the optional interfaces through http.ResponseController, which walks the Unwrap chain — so no matter how middlewares are ordered, Flush/Hijack still find the real writer.
Usage ¶
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ww := httpw.Wrap(w)
next.ServeHTTP(ww, r)
log.Printf("status=%d bytes=%d", ww.Status(), ww.BytesWritten())
})
}
A handler behind this wrapper flushes with http.NewResponseController(w), not a w.(http.Flusher) type assertion.
Status returns 0 until the handler writes a header or body, which lets a panic-recovery middleware tell "nothing sent yet" (safe to send 500) from "response already started" (must not).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Writer ¶
type Writer struct {
http.ResponseWriter
// contains filtered or unexported fields
}
Writer is an http.ResponseWriter proxy that records the response status code and byte count and can tee the body to a second writer.
It re-exposes the wrapped writer via Unwrap, so downstream code reaches the underlying writer's optional interfaces (http.Flusher, http.Hijacker, …) through http.ResponseController — which walks the Unwrap chain — instead of through this wrapper. That keeps Writer a single type rather than one per interface combination.
func Wrap ¶
func Wrap(w http.ResponseWriter) *Writer
Wrap wraps w so a middleware can observe the response. Callers that need to Flush or Hijack should use http.NewResponseController(w), not a type assertion — the Writer intentionally does not re-implement those interfaces.
func (*Writer) BytesWritten ¶
BytesWritten returns the number of body bytes written to the client.
func (*Writer) Status ¶
Status returns the recorded status code, or 0 if the handler has not written a header or body yet.
func (*Writer) Tee ¶
Tee mirrors every subsequent body write to dst in addition to the client. It is illegal to modify dst concurrently with writes.
func (*Writer) Unwrap ¶
func (w *Writer) Unwrap() http.ResponseWriter
Unwrap returns the wrapped writer so http.ResponseController can reach its optional interfaces.
func (*Writer) Write ¶
Write proxies the body: it implies a 200 on the first write when the handler never called WriteHeader, counts the bytes, and mirrors them to the tee when one is set.
func (*Writer) WriteHeader ¶
WriteHeader records the first non-1xx status code and forwards every call. Informational (1xx) responses are forwarded but not recorded as the final status, matching net/http's interim-response handling; 101 Switching Protocols is treated as a terminal status.