loggingResponseWriter has no Unwrap, so http.ResponseController cannot reach the real writer through the shipped chain #191
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found while implementing #187. Pre-existing, not caused by that work. NOT milestoned 1.0.0 — nothing in the tree flushes, hijacks or sets a write deadline today, so it is latent rather than live.
loggingResponseWriterininternal/middlewarewrapshttp.ResponseWriterand implements noUnwrap() http.ResponseWriter. Since Go 1.20,http.ResponseControllerwalks the chain viaUnwrapto reach the underlying writer's optional interfaces —Flush,Hijack,SetReadDeadline,SetWriteDeadline. A wrapper withoutUnwrapterminates that walk, soResponseControllermethods returnhttp.ErrNotSupportedfor any handler behind this middleware, and the type-assertion route (w.(http.Flusher)) fails the same way.Why it is worth fixing despite being latent: it fails silently and at a distance. The first handler that needs to stream — SSE for live event tailing, a chunked export, anything long-poll — will not fail at the wrapper. It will fail at the handler with a
nilorErrNotSupported, and the cause will be a middleware nobody was looking at. The fix is one method; the debugging session it prevents is not.The recoverer added by #189 does implement
Unwrapon its own writer wrapper, so it is not the gap — but that also means the chain is inconsistent, which is worse than uniformly wrong.Definition of done
loggingResponseWriterimplementsUnwrap() http.ResponseWriter.http.ResponseWriterwrapper in the repo is audited for the same gap, not just this one — enumerate what you found, including the ones that already comply. A partial sweep here just relocates the trap.http.ResponseController.Flush(or an equivalent optional interface) through the production middleware chain, not a hand-assembled one — the point is that the shipped chain works, and a hand-built stack is exactly how this was missed.Unwrapmust fail that test.Implementation requirements
next, PR based onnext, single commit, title ending(closes #N).TODO.md(see #112).make bootstrapin a fresh clone before gating — browser assets are fetched at build time, andmake lintneeds Docker.make checkplus the cache-defeated Docker lint path. All linting runs in Docker, never on the host. Clean up every container and image you start; never run any prune.