http2: send undeclared trailers when body is not written

Fix a bug wherein undeclared trailers (set with the "Trailer:" prefix)
were not sent when the response body is neither written nor flushed.

We were testing responseWriterState.hasTrailers before promoting
undeclared trailers into rws.trailers, resulting in the response headers
being sent with an END_STREAM flag. Promote undeclared headers earlier
so that we leave the stream open for them to be sent.

For golang/go#54723

Change-Id: Ic036925f4a7ec775282b6e474aa72249d6418b23
Reviewed-on: https://go-review.googlesource.com/c/net/+/426874
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Damien Neil <dneil@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
diff --git a/http2/server.go b/http2/server.go
index d2e52f3..967c9e7 100644
--- a/http2/server.go
+++ b/http2/server.go
@@ -2500,6 +2500,10 @@
 		rws.writeHeader(200)
 	}
 
+	if rws.handlerDone {
+		rws.promoteUndeclaredTrailers()
+	}
+
 	isHeadResp := rws.req.Method == "HEAD"
 	if !rws.sentHeader {
 		rws.sentHeader = true
@@ -2571,10 +2575,6 @@
 		return 0, nil
 	}
 
-	if rws.handlerDone {
-		rws.promoteUndeclaredTrailers()
-	}
-
 	// only send trailers if they have actually been defined by the
 	// server handler.
 	hasNonemptyTrailers := rws.hasNonemptyTrailers()
diff --git a/http2/server_test.go b/http2/server_test.go
index b77372c..654d53f 100644
--- a/http2/server_test.go
+++ b/http2/server_test.go
@@ -3055,6 +3055,30 @@
 	})
 }
 
+func TestServerWritesUndeclaredTrailers(t *testing.T) {
+	const trailer = "Trailer-Header"
+	const value = "hi1"
+	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set(http.TrailerPrefix+trailer, value)
+	}, optOnlyServer)
+	defer st.Close()
+
+	tr := &Transport{TLSClientConfig: tlsConfigInsecure}
+	defer tr.CloseIdleConnections()
+
+	cl := &http.Client{Transport: tr}
+	resp, err := cl.Get(st.ts.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
+
+	if got, want := resp.Trailer.Get(trailer), value; got != want {
+		t.Errorf("trailer %v = %q, want %q", trailer, got, want)
+	}
+}
+
 // validate transmitted header field names & values
 // golang.org/issue/14048
 func TestServerDoesntWriteInvalidHeaders(t *testing.T) {