Skip to content

Commit 8d074fa

Browse files
authored
fix(middleware): pass through http.Flusher for SSE streaming (#52)
The request-logging, metrics, and tracing middlewares each wrap the ResponseWriter to capture status/bytes, but none implemented http.Flusher. Streaming handlers (/v1/answer/treewalk?stream=true, /v1/query/stream) do w.(http.Flusher) and errored with 'response writer does not support http.Flusher'. Add Flush() (delegating to the wrapped writer) + Unwrap() to statusRecorder, metricsRecorder, and tracingRecorder so flushes propagate through the whole chain and SSE streams in real time.
1 parent 6a0d1d4 commit 8d074fa

3 files changed

Lines changed: 34 additions & 0 deletions

File tree

internal/middleware/logging.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ func (r *statusRecorder) Write(b []byte) (int, error) {
2828
return n, err
2929
}
3030

31+
// Flush propagates to the wrapped writer so SSE/streaming handlers can
32+
// flush through this middleware. Without it, w.(http.Flusher) fails and
33+
// streaming endpoints error out.
34+
func (r *statusRecorder) Flush() {
35+
if f, ok := r.ResponseWriter.(http.Flusher); ok {
36+
f.Flush()
37+
}
38+
}
39+
40+
// Unwrap exposes the wrapped writer for http.ResponseController.
41+
func (r *statusRecorder) Unwrap() http.ResponseWriter { return r.ResponseWriter }
42+
3143
// AccessLog logs one structured line per HTTP request: method, path,
3244
// status, duration, request ID, and principal ID.
3345
//

internal/middleware/metrics.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ func (r *metricsRecorder) Write(b []byte) (int, error) {
5656
return n, err
5757
}
5858

59+
// Flush propagates to the wrapped writer so SSE/streaming handlers can
60+
// flush through the metrics middleware.
61+
func (r *metricsRecorder) Flush() {
62+
if f, ok := r.ResponseWriter.(http.Flusher); ok {
63+
f.Flush()
64+
}
65+
}
66+
67+
// Unwrap exposes the wrapped writer for http.ResponseController.
68+
func (r *metricsRecorder) Unwrap() http.ResponseWriter { return r.ResponseWriter }
69+
5970
// normalizePath strips URL parameters to keep metric cardinality
6071
// bounded. Document and section IDs are replaced with a placeholder.
6172
func normalizePath(path string) string {

internal/middleware/tracing.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ func (r *tracingRecorder) WriteHeader(code int) {
6767
r.ResponseWriter.WriteHeader(code)
6868
}
6969

70+
// Flush propagates to the wrapped writer so SSE/streaming handlers can
71+
// flush through the tracing middleware.
72+
func (r *tracingRecorder) Flush() {
73+
if f, ok := r.ResponseWriter.(http.Flusher); ok {
74+
f.Flush()
75+
}
76+
}
77+
78+
// Unwrap exposes the wrapped writer for http.ResponseController.
79+
func (r *tracingRecorder) Unwrap() http.ResponseWriter { return r.ResponseWriter }
80+
7081
// TraceIDFromContext extracts the trace ID string from the current span,
7182
// useful for correlating logs with traces.
7283
func TraceIDFromContext(ctx context.Context) string {

0 commit comments

Comments
 (0)