Skip to content

Commit 53b6661

Browse files
committed
net/http/httptest: make Server.CloseClientConnections wait for conns to close
httptest.Server was rewritten during Go 1.6, but CloseClientConnections was accidentally made async in the rewrite and not caught due to lack of tests. Restore the Go 1.5 behavior and add tests. Fixes #14290 Updates #14291 Change-Id: I14f01849066785053ccca2373931bc82d78c0a13 Reviewed-on: https://go-review.googlesource.com/19432 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 7ebf653 commit 53b6661

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

src/net/http/httptest/server.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,31 @@ func (s *Server) logCloseHangDebugInfo() {
202202

203203
// CloseClientConnections closes any open HTTP connections to the test Server.
204204
func (s *Server) CloseClientConnections() {
205+
var conns int
206+
ch := make(chan bool)
207+
205208
s.mu.Lock()
206-
defer s.mu.Unlock()
207209
for c := range s.conns {
208-
s.closeConn(c)
210+
conns++
211+
s.closeConnChan(c, ch)
212+
}
213+
s.mu.Unlock()
214+
215+
// Wait for outstanding closes to finish.
216+
//
217+
// Out of paranoia for making a late change in Go 1.6, we
218+
// bound how long this can wait, since golang.org/issue/14291
219+
// isn't fully understood yet. At least this should only be used
220+
// in tests.
221+
timer := time.NewTimer(5 * time.Second)
222+
defer timer.Stop()
223+
for i := 0; i < conns; i++ {
224+
select {
225+
case <-ch:
226+
case <-timer.C:
227+
// Too slow. Give up.
228+
return
229+
}
209230
}
210231
}
211232

@@ -267,9 +288,13 @@ func (s *Server) wrap() {
267288
}
268289
}
269290

270-
// closeConn closes c. Except on plan9, which is special. See comment below.
291+
// closeConn closes c.
271292
// s.mu must be held.
272-
func (s *Server) closeConn(c net.Conn) {
293+
func (s *Server) closeConn(c net.Conn) { s.closeConnChan(c, nil) }
294+
295+
// closeConnChan is like closeConn, but takes an optional channel to receive a value
296+
// when the goroutine closing c is done.
297+
func (s *Server) closeConnChan(c net.Conn, done chan<- bool) {
273298
if runtime.GOOS == "plan9" {
274299
// Go's Plan 9 net package isn't great at unblocking reads when
275300
// their underlying TCP connections are closed. Don't trust
@@ -278,7 +303,21 @@ func (s *Server) closeConn(c net.Conn) {
278303
// resources if the syscall doesn't end up returning. Oh well.
279304
s.forgetConn(c)
280305
}
281-
go c.Close()
306+
307+
// Somewhere in the chaos of https://golang.org/cl/15151 we found that
308+
// some types of conns were blocking in Close too long (or deadlocking?)
309+
// and we had to call Close in a goroutine. I (bradfitz) forget what
310+
// that was at this point, but I suspect it was *tls.Conns, which
311+
// were later fixed in https://golang.org/cl/18572, so this goroutine
312+
// is _probably_ unnecessary now. But it's too late in Go 1.6 too remove
313+
// it with confidence.
314+
// TODO(bradfitz): try to remove it for Go 1.7. (golang.org/issue/14291)
315+
go func() {
316+
c.Close()
317+
if done != nil {
318+
done <- true
319+
}
320+
}()
282321
}
283322

284323
// forgetConn removes c from the set of tracked conns and decrements it from the

src/net/http/httptest/server_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,17 @@ func TestServerCloseBlocking(t *testing.T) {
8484

8585
ts.Close() // test we don't hang here forever.
8686
}
87+
88+
// Issue 14290
89+
func TestServerCloseClientConnections(t *testing.T) {
90+
var s *Server
91+
s = NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
92+
s.CloseClientConnections()
93+
}))
94+
defer s.Close()
95+
res, err := http.Get(s.URL)
96+
if err == nil {
97+
res.Body.Close()
98+
t.Fatal("Unexpected response: %#v", res)
99+
}
100+
}

0 commit comments

Comments
 (0)