Skip to content

Commit e02fb33

Browse files
committed
internal/http3: make responseWriter.Flush write headers if not done yet
In net/http and x/net/http2, flushing an http.ResponseWriter will also trigger headers to be written if none has been written yet at that point. This behavior is sometimes relied on when implementing streaming responses (see http://go.dev/cl/4552062); therefore, let's do this in x/net/internal/http3 too. For golang/go#70914 Change-Id: Ib914afc85df21a1cddd1d5019311d3f25d943f8a Reviewed-on: https://go-review.googlesource.com/c/net/+/742160 Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicholas Husin <husin@google.com>
1 parent da558ff commit e02fb33

2 files changed

Lines changed: 35 additions & 3 deletions

File tree

internal/http3/server.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ func (rw *responseWriter) Write(b []byte) (int, error) {
298298
}
299299

300300
func (rw *responseWriter) Flush() {
301+
rw.mu.Lock()
302+
rw.writeHeaderLockedOnce(http.StatusOK)
303+
rw.mu.Unlock()
301304
rw.bw.st.Flush()
302305
}
303306

internal/http3/server_test.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func TestServerHeader(t *testing.T) {
6565
"header-from-client": {"that", "should", "be", "echoed"},
6666
})
6767
synctest.Wait()
68-
reqStream.wantHeaders(map[string][]string{
68+
reqStream.wantHeaders(http.Header{
6969
":status": {"204"},
7070
"Header-From-Client": {"that", "should", "be", "echoed"},
7171
})
@@ -93,7 +93,7 @@ func TestServerPseudoHeader(t *testing.T) {
9393
reqStream := tc.newStream(streamTypeRequest)
9494
reqStream.writeHeaders(http.Header{":method": {"GET"}})
9595
synctest.Wait()
96-
reqStream.wantHeaders(map[string][]string{":status": {"321"}})
96+
reqStream.wantHeaders(http.Header{":status": {"321"}})
9797
reqStream.wantClosed("request is complete")
9898
})
9999
}
@@ -114,7 +114,7 @@ func TestServerInvalidHeader(t *testing.T) {
114114
reqStream := tc.newStream(streamTypeRequest)
115115
reqStream.writeHeaders(http.Header{})
116116
synctest.Wait()
117-
reqStream.wantHeaders(map[string][]string{
117+
reqStream.wantHeaders(http.Header{
118118
":status": {"200"},
119119
"Valid-Name": {"valid value"},
120120
"Valid-Name-2": {"valid value 2"},
@@ -234,6 +234,35 @@ func TestServerHandlerFlushing(t *testing.T) {
234234
})
235235
}
236236

237+
func TestServerHandlerStreaming(t *testing.T) {
238+
synctest.Test(t, func(t *testing.T) {
239+
stream := make(chan string)
240+
ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
241+
// Flushing when we have not written anything yet implicitly calls
242+
// w.WriteHeader(200).
243+
w.(http.Flusher).Flush()
244+
for str := range stream {
245+
w.Write([]byte(str))
246+
w.(http.Flusher).Flush()
247+
}
248+
}))
249+
tc := ts.connect()
250+
tc.greet()
251+
252+
reqStream := tc.newStream(streamTypeRequest)
253+
reqStream.writeHeaders(http.Header{":method": {http.MethodGet}})
254+
synctest.Wait()
255+
reqStream.wantHeaders(http.Header{":status": {"200"}})
256+
257+
for _, data := range []string{"a", "bunch", "of", "things", "to", "stream"} {
258+
stream <- data
259+
reqStream.wantData([]byte(data))
260+
}
261+
close(stream)
262+
reqStream.wantClosed("request is complete")
263+
})
264+
}
265+
237266
type testServer struct {
238267
t testing.TB
239268
s *Server

0 commit comments

Comments
 (0)