Skip to content

[duplicate-code] Duplicate Code Pattern: HTTP Response Writer Status-Code Wrapper in server and tracing packages #6072

@github-actions

Description

@github-actions

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

  1. 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
  2. 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

  • Review duplication findings
  • Decide on extraction vs alignment approach
  • Implement chosen approach
  • Run make agent-finished to verify no regressions

Parent Issue

See parent analysis report: #6070
Related to #6070

Generated by Duplicate Code Detector · ● 1.9M ·

  • expires on May 27, 2026, 3:41 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions