Skip to content

Commit c7e0dda

Browse files
committed
net/http: add Server.ReadHeaderTimeout, IdleTimeout, document WriteTimeout
Updates #14204 Updates #16450 Updates #16100 Change-Id: Ic283bcec008a8e0bfbcfd8531d30fffe71052531 Reviewed-on: https://go-review.googlesource.com/32024 Reviewed-by: Tom Bergan <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 1625da2 commit c7e0dda

File tree

2 files changed

+125
-13
lines changed

2 files changed

+125
-13
lines changed

src/net/http/serve_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -4778,3 +4778,57 @@ func TestConcurrentServerServe(t *testing.T) {
47784778
go func() { srv.Serve(ln2) }()
47794779
}
47804780
}
4781+
4782+
func TestServerIdleTimeout(t *testing.T) {
4783+
if testing.Short() {
4784+
t.Skip("skipping in short mode")
4785+
}
4786+
defer afterTest(t)
4787+
ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
4788+
io.Copy(ioutil.Discard, r.Body)
4789+
io.WriteString(w, r.RemoteAddr)
4790+
}))
4791+
ts.Config.ReadHeaderTimeout = 1 * time.Second
4792+
ts.Config.IdleTimeout = 2 * time.Second
4793+
ts.Start()
4794+
defer ts.Close()
4795+
4796+
tr := &Transport{}
4797+
defer tr.CloseIdleConnections()
4798+
c := &Client{Transport: tr}
4799+
4800+
get := func() string {
4801+
res, err := c.Get(ts.URL)
4802+
if err != nil {
4803+
t.Fatal(err)
4804+
}
4805+
defer res.Body.Close()
4806+
slurp, err := ioutil.ReadAll(res.Body)
4807+
if err != nil {
4808+
t.Fatal(err)
4809+
}
4810+
return string(slurp)
4811+
}
4812+
4813+
a1, a2 := get(), get()
4814+
if a1 != a2 {
4815+
t.Fatalf("did requests on different connections")
4816+
}
4817+
time.Sleep(3 * time.Second)
4818+
a3 := get()
4819+
if a2 == a3 {
4820+
t.Fatal("request three unexpectedly on same connection")
4821+
}
4822+
4823+
// And test that ReadHeaderTimeout still works:
4824+
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
4825+
if err != nil {
4826+
t.Fatal(err)
4827+
}
4828+
defer conn.Close()
4829+
conn.Write([]byte("GET / HTTP/1.1\r\nHost: foo.com\r\n"))
4830+
time.Sleep(2 * time.Second)
4831+
if _, err := io.CopyN(ioutil.Discard, conn, 1); err == nil {
4832+
t.Fatal("copy byte succeeded; want err")
4833+
}
4834+
}

src/net/http/server.go

+71-13
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,6 @@ type conn struct {
237237
r *connReader
238238

239239
// bufr reads from r.
240-
// Users of bufr must hold mu.
241240
bufr *bufio.Reader
242241

243242
// bufw writes to checkConnErrorWriter{c}, which populates werr on error.
@@ -247,11 +246,11 @@ type conn struct {
247246
// on this connection, if any.
248247
lastMethod string
249248

250-
// mu guards hijackedv, use of bufr, (*response).closeNotifyCh.
251-
mu sync.Mutex
252-
253249
curReq atomic.Value // of *response (which has a Request in it)
254250

251+
// mu guards hijackedv
252+
mu sync.Mutex
253+
255254
// hijackedv is whether this connection has been hijacked
256255
// by a Handler with the Hijacker interface.
257256
// It is guarded by mu.
@@ -426,7 +425,7 @@ type response struct {
426425

427426
// closeNotifyCh is the channel returned by CloseNotify.
428427
// TODO(bradfitz): this is currently (for Go 1.8) always
429-
// non-nil. Make this lazily-created again as it used to be.
428+
// non-nil. Make this lazily-created again as it used to be?
430429
closeNotifyCh chan bool
431430
didCloseNotify int32 // atomic (only 0->1 winner should send)
432431
}
@@ -847,24 +846,31 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
847846
return nil, ErrHijacked
848847
}
849848

849+
var (
850+
wholeReqDeadline time.Time // or zero if none
851+
hdrDeadline time.Time // or zero if none
852+
)
853+
t0 := time.Now()
854+
if d := c.server.readHeaderTimeout(); d != 0 {
855+
hdrDeadline = t0.Add(d)
856+
}
850857
if d := c.server.ReadTimeout; d != 0 {
851-
c.rwc.SetReadDeadline(time.Now().Add(d))
858+
wholeReqDeadline = t0.Add(d)
852859
}
860+
c.rwc.SetReadDeadline(hdrDeadline)
853861
if d := c.server.WriteTimeout; d != 0 {
854862
defer func() {
855863
c.rwc.SetWriteDeadline(time.Now().Add(d))
856864
}()
857865
}
858866

859867
c.r.setReadLimit(c.server.initialReadLimitSize())
860-
c.mu.Lock() // while using bufr
861868
if c.lastMethod == "POST" {
862869
// RFC 2616 section 4.1 tolerance for old buggy clients.
863870
peek, _ := c.bufr.Peek(4) // ReadRequest will get err below
864871
c.bufr.Discard(numLeadingCRorLF(peek))
865872
}
866873
req, err := readRequest(c.bufr, keepHostHeader)
867-
c.mu.Unlock()
868874
if err != nil {
869875
if c.r.hitReadLimit() {
870876
return nil, errTooLarge
@@ -910,6 +916,11 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
910916
body.doEarlyClose = true
911917
}
912918

919+
// Adjust the read deadline if necessary.
920+
if !hdrDeadline.Equal(wholeReqDeadline) {
921+
c.rwc.SetReadDeadline(wholeReqDeadline)
922+
}
923+
913924
w = &response{
914925
conn: c,
915926
cancelCtx: cancelCtx,
@@ -1710,6 +1721,14 @@ func (c *conn) serve(ctx context.Context) {
17101721
}
17111722
c.setState(c.rwc, StateIdle)
17121723
c.curReq.Store((*response)(nil))
1724+
1725+
if d := c.server.idleTimeout(); d != 0 {
1726+
c.rwc.SetReadDeadline(time.Now().Add(d))
1727+
if _, err := c.bufr.Peek(4); err != nil {
1728+
return
1729+
}
1730+
}
1731+
c.rwc.SetReadDeadline(time.Time{})
17131732
}
17141733
}
17151734

@@ -2168,11 +2187,36 @@ func Serve(l net.Listener, handler Handler) error {
21682187
// A Server defines parameters for running an HTTP server.
21692188
// The zero value for Server is a valid configuration.
21702189
type Server struct {
2171-
Addr string // TCP address to listen on, ":http" if empty
2172-
Handler Handler // handler to invoke, http.DefaultServeMux if nil
2173-
ReadTimeout time.Duration // maximum duration before timing out read of the request
2174-
WriteTimeout time.Duration // maximum duration before timing out write of the response
2175-
TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS
2190+
Addr string // TCP address to listen on, ":http" if empty
2191+
Handler Handler // handler to invoke, http.DefaultServeMux if nil
2192+
TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS
2193+
2194+
// ReadTimeout is the maximum duration for reading the entire
2195+
// request, including the body.
2196+
//
2197+
// Because ReadTimeout does not let Handlers make per-request
2198+
// decisions on each request body's acceptable deadline or
2199+
// upload rate, most users will prefer to use
2200+
// ReadHeaderTimeout. It is valid to use them both.
2201+
ReadTimeout time.Duration
2202+
2203+
// ReadHeaderTimeout is the amount of time allowed to read
2204+
// request headers. The connection's read deadline is reset
2205+
// after reading the headers and the Handler can decide what
2206+
// is considered too slow for the body.
2207+
ReadHeaderTimeout time.Duration
2208+
2209+
// WriteTimeout is the maximum duration before timing out
2210+
// writes of the response. It is reset whenever a new
2211+
// request's header is read. Like ReadTimeout, it does not
2212+
// let Handlers make decisions on a per-request basis.
2213+
WriteTimeout time.Duration
2214+
2215+
// IdleTimeout is the maximum amount of time to wait for the
2216+
// next request when keep-alives are enabled. If IdleTimeout
2217+
// is zero, the value of ReadTimeout is used. If both are
2218+
// zero, there is no timeout.
2219+
IdleTimeout time.Duration
21762220

21772221
// MaxHeaderBytes controls the maximum number of bytes the
21782222
// server will read parsing the request header's keys and
@@ -2366,6 +2410,20 @@ func (srv *Server) Serve(l net.Listener) error {
23662410
}
23672411
}
23682412

2413+
func (s *Server) idleTimeout() time.Duration {
2414+
if s.IdleTimeout != 0 {
2415+
return s.IdleTimeout
2416+
}
2417+
return s.ReadTimeout
2418+
}
2419+
2420+
func (s *Server) readHeaderTimeout() time.Duration {
2421+
if s.ReadHeaderTimeout != 0 {
2422+
return s.ReadHeaderTimeout
2423+
}
2424+
return s.ReadTimeout
2425+
}
2426+
23692427
func (s *Server) doKeepAlives() bool {
23702428
return atomic.LoadInt32(&s.disableKeepAlives) == 0
23712429
}

0 commit comments

Comments
 (0)