diff --git a/src/net/http/transport.go b/src/net/http/transport.go index f3cf31c8a7..dd61617fd1 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -929,16 +929,37 @@ func (t *Transport) queueForIdleConn(w *wantConn) (delivered bool) { return false } + // If IdleConnTimeout is set, calculate the oldest + // persistConn.idleAt time we're willing to use a cached idle + // conn. + var oldTime time.Time + if t.IdleConnTimeout > 0 { + oldTime = time.Now().Add(-t.IdleConnTimeout) + } + // Look for most recently-used idle connection. if list, ok := t.idleConn[w.key]; ok { stop := false delivered := false for len(list) > 0 && !stop { pconn := list[len(list)-1] - if pconn.isBroken() { - // persistConn.readLoop has marked the connection broken, - // but Transport.removeIdleConn has not yet removed it from the idle list. - // Drop on floor on behalf of Transport.removeIdleConn. + + // See whether this connection has been idle too long, considering + // only the wall time (the Round(0)), in case this is a laptop or VM + // coming out of suspend with previously cached idle connections. + tooOld := !oldTime.IsZero() && pconn.idleAt.Round(0).Before(oldTime) + if tooOld { + // Async cleanup. Launch in its own goroutine (as if a + // time.AfterFunc called it); it acquires idleMu, which we're + // holding, and does a synchronous net.Conn.Close. + go pconn.closeConnIfStillIdle() + } + if pconn.isBroken() || tooOld { + // If either persistConn.readLoop has marked the connection + // broken, but Transport.removeIdleConn has not yet removed it + // from the idle list, or if this persistConn is too old (it was + // idle too long), then ignore it and look for another. In both + // cases it's already in the process of being closed. list = list[:len(list)-1] continue }