mirror of
https://github.com/golang/go
synced 2024-11-18 15:14:44 -07:00
net/http: fix memory leak in Transport
Fixes #5794 R=golang-dev, r CC=golang-dev https://golang.org/cl/10747044
This commit is contained in:
parent
64441d6d66
commit
46161cd079
@ -48,6 +48,12 @@ func (t *Transport) IdleConnCountForTesting(cacheKey string) int {
|
||||
return len(conns)
|
||||
}
|
||||
|
||||
func (t *Transport) IdleConnChMapSizeForTesting() int {
|
||||
t.idleMu.Lock()
|
||||
defer t.idleMu.Unlock()
|
||||
return len(t.idleConnCh)
|
||||
}
|
||||
|
||||
func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
|
||||
f := func() <-chan time.Time {
|
||||
return ch
|
||||
|
@ -217,6 +217,7 @@ func (t *Transport) CloseIdleConnections() {
|
||||
t.idleMu.Lock()
|
||||
m := t.idleConn
|
||||
t.idleConn = nil
|
||||
t.idleConnCh = nil
|
||||
t.idleMu.Unlock()
|
||||
if m == nil {
|
||||
return
|
||||
@ -295,8 +296,10 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
|
||||
max = DefaultMaxIdleConnsPerHost
|
||||
}
|
||||
t.idleMu.Lock()
|
||||
|
||||
waitingDialer := t.idleConnCh[key]
|
||||
select {
|
||||
case t.idleConnCh[key] <- pconn:
|
||||
case waitingDialer <- pconn:
|
||||
// We're done with this pconn and somebody else is
|
||||
// currently waiting for a conn of this type (they're
|
||||
// actively dialing, but this conn is ready
|
||||
@ -305,6 +308,11 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
|
||||
t.idleMu.Unlock()
|
||||
return true
|
||||
default:
|
||||
if waitingDialer != nil {
|
||||
// They had populated this, but their dial won
|
||||
// first, so we can clean up this map entry.
|
||||
delete(t.idleConnCh, key)
|
||||
}
|
||||
}
|
||||
if t.idleConn == nil {
|
||||
t.idleConn = make(map[string][]*persistConn)
|
||||
@ -324,7 +332,13 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// getIdleConnCh returns a channel to receive and return idle
|
||||
// persistent connection for the given connectMethod.
|
||||
// It may return nil, if persistent connections are not being used.
|
||||
func (t *Transport) getIdleConnCh(cm *connectMethod) chan *persistConn {
|
||||
if t.DisableKeepAlives {
|
||||
return nil
|
||||
}
|
||||
key := cm.key()
|
||||
t.idleMu.Lock()
|
||||
defer t.idleMu.Unlock()
|
||||
|
@ -1575,6 +1575,41 @@ func TestProxyFromEnvironment(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdleConnChannelLeak(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
var n int
|
||||
|
||||
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||
mu.Lock()
|
||||
n++
|
||||
mu.Unlock()
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
tr := &Transport{
|
||||
Dial: func(netw, addr string) (net.Conn, error) {
|
||||
return net.Dial(netw, ts.Listener.Addr().String())
|
||||
},
|
||||
}
|
||||
defer tr.CloseIdleConnections()
|
||||
|
||||
c := &Client{Transport: tr}
|
||||
|
||||
// First, without keep-alives.
|
||||
for _, disableKeep := range []bool{true, false} {
|
||||
tr.DisableKeepAlives = disableKeep
|
||||
for i := 0; i < 5; i++ {
|
||||
_, err := c.Get(fmt.Sprintf("http://foo-host-%d.tld/", i))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if got := tr.IdleConnChMapSizeForTesting(); got != 0 {
|
||||
t.Fatalf("ForDisableKeepAlives = %v, map size = %d; want 0", disableKeep, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rgz is a gzip quine that uncompresses to itself.
|
||||
var rgz = []byte{
|
||||
0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
|
||||
|
Loading…
Reference in New Issue
Block a user