Let a handler's flush reach the client through the access log (closes #191)
check / check (push) Successful in 3m15s

The access log's response writer had no Unwrap method, so
http.ResponseController stopped at it and a handler's Flush returned
http.ErrNotSupported. It now has one.

With metrics on, adding Unwrap alone is not enough: go-http-metrics'
writer only passes a flush on when the writer inside it has a Flush
method, and it sat inside Logging, so the flush silently did nothing.
Metrics is now registered outside Logging, and the README's middleware
list follows.

A new test flushes through the production router, with the defaults
and with metrics and Sentry on, on a global route and inside an admin
page route group.

Model: opus-5-5
This commit is contained in:
2026-10-02 08:46:08 +00:00
parent 5b1d283d06
commit e88192aa9a
5 changed files with 119 additions and 9 deletions
+7
View File
@@ -233,6 +233,13 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.ResponseWriter.WriteHeader(code)
}
// Unwrap lets http.ResponseController reach the writer underneath, so
// a handler can still flush or set a write deadline through the access
// log.
func (lrw *loggingResponseWriter) Unwrap() http.ResponseWriter {
return lrw.ResponseWriter
}
// concreteLogURL renders the request's own URL for the access log
// branches that keep it, with the query string replaced by a fixed
// marker.
+3 -5
View File
@@ -627,11 +627,9 @@ func TestRecovererIgnoresANonPanickingHandler(t *testing.T) {
// net/http's own writer from http.ResponseController, so a handler
// that flushes or sets a deadline starts failing.
//
// The recoverer is the only middleware in the chain here. The access
// logger's own wrapper does not implement Unwrap, so a chain
// containing it fails this regardless of what the recoverer does;
// what is being pinned is that the recoverer adds no such opacity of
// its own.
// The recoverer is the only middleware in the chain here;
// TestFlushThroughProductionRouter in internal/server covers the
// shipped chain.
func TestRecovererKeepsResponseControllerWorking(t *testing.T) {
t.Parallel()