From 039b87fac41ca283465e12a3bcc170ccd6c92f84 Mon Sep 17 00:00:00 2001 From: "Nicholas S. Husin" Date: Tue, 3 Mar 2026 13:07:09 -0500 Subject: [PATCH] internal/http3: return error when Write is used after status 304 is set In our HTTP/1 and HTTP/2 implementations, calling Write in a server handler after WriteHeader has been called with status 304 will return an http.ErrBodyNotAllowed error. This change adds the same behavior for the HTTP/3 server. For golang/go#70914 Change-Id: I6be926412d51217a8b88b2ad4ce79935dd3e7af7 Reviewed-on: https://go-review.googlesource.com/c/net/+/751140 Reviewed-by: Nicholas Husin Reviewed-by: Damien Neil LUCI-TryBot-Result: Go LUCI --- internal/http3/server.go | 4 ++++ internal/http3/server_test.go | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/internal/http3/server.go b/internal/http3/server.go index 9d8937d1..8c960809 100644 --- a/internal/http3/server.go +++ b/internal/http3/server.go @@ -463,6 +463,10 @@ func (rw *responseWriter) Write(b []byte) (n int, err error) { rw.mu.Lock() defer rw.mu.Unlock() + if rw.statusCode == http.StatusNotModified { + return 0, http.ErrBodyNotAllowed + } + b, trimmed := rw.trimWriteLocked(b) if trimmed { defer func() { diff --git a/internal/http3/server_test.go b/internal/http3/server_test.go index 7b938a74..f5216968 100644 --- a/internal/http3/server_test.go +++ b/internal/http3/server_test.go @@ -5,6 +5,7 @@ package http3 import ( + "errors" "io" "maps" "net/http" @@ -912,6 +913,25 @@ func TestServer103EarlyHints(t *testing.T) { }) } +func TestServer304NotModified(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotModified) + if _, err := w.Write([]byte("body should not be allowed")); !errors.Is(err, http.ErrBodyNotAllowed) { + t.Errorf("got %v error when calling Write after WriteHeader(304), want %v error", err, http.ErrBodyNotAllowed) + } + })) + tc := ts.connect() + tc.greet() + + reqStream := tc.newStream(streamTypeRequest) + reqStream.writeHeaders(requestHeader(nil)) + synctest.Wait() + reqStream.wantSomeHeaders(http.Header{":status": {"304"}}) + reqStream.wantClosed("request is complete") + }) +} + type testServer struct { t testing.TB s *Server