src/net/http/clientserver_test.go | 17 ++++++++++++++++- src/net/http/serve_test.go | 29 +++++++++++++++++++++++++++++ src/net/http/server.go | 2 ++ diff --git a/src/net/http/clientserver_test.go b/src/net/http/clientserver_test.go index 2bca1d3253648b42cefb9c850b17aa42626a82d1..dbe1309b06187acf441a85257fdfca23d780576a 100644 --- a/src/net/http/clientserver_test.go +++ b/src/net/http/clientserver_test.go @@ -46,6 +46,11 @@ http2Mode = testMode("h2") // HTTP/2 http2UnencryptedMode = testMode("h2unencrypted") // HTTP/2 ) +type ( + testAddMode []testMode // default, plus these + testSkipMode []testMode // default, minus these +) + func (m testMode) Scheme() string { switch m { case http1Mode, http2UnencryptedMode: @@ -80,6 +85,16 @@ modes := []testMode{http1Mode, http2Mode} parallel := true for _, opt := range opts { switch opt := opt.(type) { + case testAddMode: + for _, m := range opt { + if !slices.Contains(modes, m) { + modes = append(modes, m) + } + } + case testSkipMode: + modes = slices.DeleteFunc(modes, func(m testMode) bool { + return slices.Contains(opt, m) + }) case []testMode: modes = opt case testNotParallelOpt: @@ -178,7 +193,7 @@ // // The optFakeNet option configures the server and client to use a fake network implementation, // suitable for use in testing/synctest tests. func newClientServerTest(t testing.TB, mode testMode, h Handler, opts ...any) *clientServerTest { - if mode == http2Mode { + if mode == http2Mode || mode == http2UnencryptedMode { CondSkipHTTP2(t) } cst := &clientServerTest{ diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index d26d7c60eebc5258b6797b8c726aa1f1a42dcad3..5673dc1a9c079d5ebfe3342128db3be2166561f3 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -872,6 +872,35 @@ }) } } +func TestServerReadHeaderTimeoutIsCleared(t *testing.T) { + runSynctest(t, testServerReadHeaderTimeoutIsCleared, + testAddMode{http2UnencryptedMode}) +} +func testServerReadHeaderTimeoutIsCleared(t *testing.T, mode testMode) { + const timeout = time.Second + cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) { + w.WriteHeader(200) + NewResponseController(w).Flush() + time.Sleep(2 * timeout) + io.WriteString(w, "ok") + }), func(s *Server) { + s.ReadHeaderTimeout = timeout + }, optFakeNet) + + res, err := cst.c.Get(cst.ts.URL) + if err != nil { + t.Fatal(err) + } + got, err := io.ReadAll(res.Body) + res.Body.Close() + if err != nil { + t.Fatalf("reading response body after ReadHeaderTimeout: %v", err) + } + if want := "ok"; string(got) != want { + t.Fatalf("response body = %q, want %q", got, want) + } +} + func TestServerReadTimeout(t *testing.T) { run(t, testServerReadTimeout) } func testServerReadTimeout(t *testing.T, mode testMode) { respBody := "response body" diff --git a/src/net/http/server.go b/src/net/http/server.go index 6a60d941229f989a015e4e30b89c25f248fdea47..0ea4ca3e47ff417e337860d5dcbecc8abaeaa9f1 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -2171,6 +2171,8 @@ if !hasPreface(c, []byte("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")) { return false } c.setState(c.rwc, StateActive, skipHooks) + c.rwc.SetReadDeadline(time.Time{}) + c.rwc.SetWriteDeadline(time.Time{}) h := unencryptedHTTP2Request{ctx, c.rwc, serverHandler{c.server}} fn(c.server, unencryptedTLSConn(c.rwc), h) return true