Part of duplicate code analysis: #6070
Summary
internal/server/response_writer.go (responseWriter) and internal/tracing/http.go (statusResponseWriter) both independently implement an http.ResponseWriter wrapper that captures the HTTP status code via WriteHeader. The core status-capture logic (~12 lines) is structurally identical in both types.
Duplication Details
Pattern: HTTP response-writer wrapper with status-code capture
- Severity: Low-Medium
- Occurrences: 2 types with overlapping
WriteHeader/Write implementation
- Locations:
internal/server/response_writer.go (lines 14–50) — responseWriter
internal/tracing/http.go (lines 18–48) — statusResponseWriter
Shared structural code in both types:
// Both types embed http.ResponseWriter and capture statusCode
type xWriter struct {
http.ResponseWriter
statusCode int
}
func (w *xWriter) WriteHeader(code int) {
w.statusCode = code
w.ResponseWriter.WriteHeader(code)
}
// server/response_writer.go also captures body bytes
// tracing/http.go also has Unwrap() for interface delegation
Key differences:
responseWriter additionally captures the response body in a bytes.Buffer and exposes Body() / StatusCode() accessors (used for debug logging)
statusResponseWriter additionally implements Unwrap() so that http.ResponseController callers can access optional interfaces (e.g. http.Flusher)
Impact Analysis
- Maintainability: A subtle behavioral change to
WriteHeader (e.g. handling implicit 200 on first Write) was added to statusResponseWriter but not to responseWriter, showing the types have already diverged
- Bug Risk: Low — the two types serve different purposes, but sharing a base could prevent future divergence bugs
- Code Bloat: ~12 duplicated lines
Refactoring Recommendations
-
Extract a baseResponseWriter in internal/httputil/
// BaseResponseWriter wraps http.ResponseWriter and captures the status code.
type BaseResponseWriter struct {
http.ResponseWriter
StatusCode int
}
func (w *BaseResponseWriter) WriteHeader(code int) {
w.StatusCode = code
w.ResponseWriter.WriteHeader(code)
}
func (w *BaseResponseWriter) Write(b []byte) (int, error) {
if w.StatusCode == 0 { w.StatusCode = http.StatusOK }
return w.ResponseWriter.Write(b)
}
func (w *BaseResponseWriter) Unwrap() http.ResponseWriter { return w.ResponseWriter }
server/response_writer.go embeds httputil.BaseResponseWriter and adds body capture
tracing/http.go embeds httputil.BaseResponseWriter (or uses it directly)
- Estimated effort: 1–2 hours
-
Alternative (lower effort): Add Unwrap() to server/response_writer.go to bring it in line with statusResponseWriter without a full extraction, reducing behavioral divergence.
Implementation Checklist
Parent Issue
See parent analysis report: #6070
Related to #6070
Generated by Duplicate Code Detector · ● 1.9M · ◷
Part of duplicate code analysis: #6070
Summary
internal/server/response_writer.go(responseWriter) andinternal/tracing/http.go(statusResponseWriter) both independently implement anhttp.ResponseWriterwrapper that captures the HTTP status code viaWriteHeader. The core status-capture logic (~12 lines) is structurally identical in both types.Duplication Details
Pattern: HTTP response-writer wrapper with status-code capture
WriteHeader/Writeimplementationinternal/server/response_writer.go(lines 14–50) —responseWriterinternal/tracing/http.go(lines 18–48) —statusResponseWriterShared structural code in both types:
Key differences:
responseWriteradditionally captures the response body in abytes.Bufferand exposesBody()/StatusCode()accessors (used for debug logging)statusResponseWriteradditionally implementsUnwrap()so thathttp.ResponseControllercallers can access optional interfaces (e.g.http.Flusher)Impact Analysis
WriteHeader(e.g. handling implicit 200 on firstWrite) was added tostatusResponseWriterbut not toresponseWriter, showing the types have already divergedRefactoring Recommendations
Extract a
baseResponseWriterininternal/httputil/server/response_writer.goembedshttputil.BaseResponseWriterand adds body capturetracing/http.goembedshttputil.BaseResponseWriter(or uses it directly)Alternative (lower effort): Add
Unwrap()toserver/response_writer.goto bring it in line withstatusResponseWriterwithout a full extraction, reducing behavioral divergence.Implementation Checklist
make agent-finishedto verify no regressionsParent Issue
See parent analysis report: #6070
Related to #6070