diff --git a/CHANGELOG.md b/CHANGELOG.md index 4912a5b27de..038445e6c04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed +- Superfluous call to `WriteHeader` when flushing after setting a status code in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#6074) - Superfluous call to `WriteHeader` when writing the response body after setting a status code in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#6055) diff --git a/instrumentation/net/http/otelhttp/internal/request/resp_writer_wrapper.go b/instrumentation/net/http/otelhttp/internal/request/resp_writer_wrapper.go index c5f2fa80524..fbc344cbdda 100644 --- a/instrumentation/net/http/otelhttp/internal/request/resp_writer_wrapper.go +++ b/instrumentation/net/http/otelhttp/internal/request/resp_writer_wrapper.go @@ -82,7 +82,12 @@ func (w *RespWriterWrapper) writeHeader(statusCode int) { // Flush implements [http.Flusher]. func (w *RespWriterWrapper) Flush() { - w.WriteHeader(http.StatusOK) + w.mu.Lock() + defer w.mu.Unlock() + + if !w.wroteHeader { + w.writeHeader(http.StatusOK) + } if f, ok := w.ResponseWriter.(http.Flusher); ok { f.Flush() diff --git a/instrumentation/net/http/otelhttp/test/handler_test.go b/instrumentation/net/http/otelhttp/test/handler_test.go index 780ec6fab54..831db4c0a85 100644 --- a/instrumentation/net/http/otelhttp/test/handler_test.go +++ b/instrumentation/net/http/otelhttp/test/handler_test.go @@ -215,6 +215,12 @@ func (rw *respWriteHeaderCounter) WriteHeader(statusCode int) { rw.ResponseWriter.WriteHeader(statusCode) } +func (rw *respWriteHeaderCounter) Flush() { + if f, ok := rw.ResponseWriter.(http.Flusher); ok { + f.Flush() + } +} + func TestHandlerPropagateWriteHeaderCalls(t *testing.T) { testCases := []struct { name string @@ -265,6 +271,23 @@ func TestHandlerPropagateWriteHeaderCalls(t *testing.T) { }, expectHeadersWritten: []int{http.StatusBadRequest}, }, + { + name: "When writing the header indirectly through flush", + handler: func(w http.ResponseWriter, r *http.Request) { + f := w.(http.Flusher) + f.Flush() + }, + expectHeadersWritten: []int{http.StatusOK}, + }, + { + name: "With a header already written when flushing", + handler: func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadRequest) + f := w.(http.Flusher) + f.Flush() + }, + expectHeadersWritten: []int{http.StatusBadRequest}, + }, } for _, tc := range testCases {