Skip to content

Commit 4e7e7ae

Browse files
hawkwmvdan
authored andcommitted
net/http: don't time out idle server connections after ReadHeaderTimeout
Consistently wait for idle connections to become readable before starting the ReadHeaderTimeout timer. Previously, connections with no idle timeout skipped directly to reading headers, so the ReadHeaderTimeout also included time spent idle. Fixes #54784 Change-Id: Iff1a876f00311d03dfa0fbef5b577506c62f7c41 GitHub-Last-Rev: 0933274 GitHub-Pull-Request: #54785 Reviewed-on: https://go-review.googlesource.com/c/go/+/426895 Run-TryBot: Damien Neil <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Damien Neil <[email protected]> Reviewed-by: Benny Siegert <[email protected]>
1 parent a295890 commit 4e7e7ae

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

src/net/http/serve_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5843,6 +5843,58 @@ func TestServerCancelsReadTimeoutWhenIdle(t *testing.T) {
58435843
})
58445844
}
58455845

5846+
// Issue 54784: test that the Server's ReadHeaderTimeout only starts once the
5847+
// beginning of a request has been received, rather than including time the
5848+
// connection spent idle.
5849+
func TestServerCancelsReadHeaderTimeoutWhenIdle(t *testing.T) {
5850+
setParallel(t)
5851+
defer afterTest(t)
5852+
runTimeSensitiveTest(t, []time.Duration{
5853+
10 * time.Millisecond,
5854+
50 * time.Millisecond,
5855+
250 * time.Millisecond,
5856+
time.Second,
5857+
2 * time.Second,
5858+
}, func(t *testing.T, timeout time.Duration) error {
5859+
ts := httptest.NewUnstartedServer(serve(200))
5860+
ts.Config.ReadHeaderTimeout = timeout
5861+
ts.Config.IdleTimeout = 0 // disable idle timeout
5862+
ts.Start()
5863+
defer ts.Close()
5864+
5865+
// rather than using an http.Client, create a single connection, so that
5866+
// we can ensure this connection is not closed.
5867+
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
5868+
if err != nil {
5869+
t.Fatalf("dial failed: %v", err)
5870+
}
5871+
br := bufio.NewReader(conn)
5872+
defer conn.Close()
5873+
5874+
if _, err := conn.Write([]byte("GET / HTTP/1.1\r\nHost: e.com\r\n\r\n")); err != nil {
5875+
t.Fatalf("writing first request failed: %v", err)
5876+
}
5877+
5878+
if _, err := ReadResponse(br, nil); err != nil {
5879+
t.Fatalf("first response (before timeout) failed: %v", err)
5880+
}
5881+
5882+
// wait for longer than the server's ReadHeaderTimeout, and then send
5883+
// another request
5884+
time.Sleep(timeout + 10*time.Millisecond)
5885+
5886+
if _, err := conn.Write([]byte("GET / HTTP/1.1\r\nHost: e.com\r\n\r\n")); err != nil {
5887+
t.Fatalf("writing second request failed: %v", err)
5888+
}
5889+
5890+
if _, err := ReadResponse(br, nil); err != nil {
5891+
t.Fatalf("second response (after timeout) failed: %v", err)
5892+
}
5893+
5894+
return nil
5895+
})
5896+
}
5897+
58465898
// runTimeSensitiveTest runs test with the provided durations until one passes.
58475899
// If they all fail, t.Fatal is called with the last one's duration and error value.
58485900
func runTimeSensitiveTest(t *testing.T, durations []time.Duration, test func(t *testing.T, d time.Duration) error) {

src/net/http/server.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2002,10 +2002,18 @@ func (c *conn) serve(ctx context.Context) {
20022002

20032003
if d := c.server.idleTimeout(); d != 0 {
20042004
c.rwc.SetReadDeadline(time.Now().Add(d))
2005-
if _, err := c.bufr.Peek(4); err != nil {
2006-
return
2007-
}
2005+
} else {
2006+
c.rwc.SetReadDeadline(time.Time{})
20082007
}
2008+
2009+
// Wait for the connection to become readable again before trying to
2010+
// read the next request. This prevents a ReadHeaderTimeout or
2011+
// ReadTimeout from starting until the first bytes of the next request
2012+
// have been received.
2013+
if _, err := c.bufr.Peek(4); err != nil {
2014+
return
2015+
}
2016+
20092017
c.rwc.SetReadDeadline(time.Time{})
20102018
}
20112019
}

0 commit comments

Comments
 (0)