httpw

package
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: MIT Imports: 2 Imported by: 0

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

func (w *Writer) BytesWritten() int

BytesWritten returns the number of body bytes written to the client.

func (*Writer) Status

func (w *Writer) Status() int

Status returns the recorded status code, or 0 if the handler has not written a header or body yet.

func (*Writer) Tee

func (w *Writer) Tee(dst io.Writer)

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

func (w *Writer) Write(b []byte) (int, error)

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

func (w *Writer) WriteHeader(code int)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL