Commit a6d4471b authored by Colby Ranger's avatar Colby Ranger Committed by Brad Fitzpatrick

net/http/httputil: Made reverseproxy test less flaky.

The reverseproxy test depended on the behavior of
runtime.NumGoroutines(), which makes no guarantee when
goroutines are reaped. Instead, modify the flushLoop()
to invoke a callback when it returns, so the exit
from the loop can be tested, instead of the number
of gorountines running.

R=bradfitz
CC=golang-dev
https://golang.org/cl/6068046
parent dda6d6aa
...@@ -17,9 +17,9 @@ import ( ...@@ -17,9 +17,9 @@ import (
"time" "time"
) )
// beforeCopyResponse is a callback set by tests to intercept the state of the // onExitFlushLoop is a callback set by tests to detect the state of the
// output io.Writer before the data is copied to it. // flushLoop() goroutine.
var beforeCopyResponse func(dst io.Writer) var onExitFlushLoop func()
// ReverseProxy is an HTTP Handler that takes an incoming request and // ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the // sends it to another server, proxying the response back to the
...@@ -138,9 +138,6 @@ func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) { ...@@ -138,9 +138,6 @@ func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
} }
} }
if beforeCopyResponse != nil {
beforeCopyResponse(dst)
}
io.Copy(dst, src) io.Copy(dst, src)
} }
...@@ -169,6 +166,9 @@ func (m *maxLatencyWriter) flushLoop() { ...@@ -169,6 +166,9 @@ func (m *maxLatencyWriter) flushLoop() {
for { for {
select { select {
case <-m.done: case <-m.done:
if onExitFlushLoop != nil {
onExitFlushLoop()
}
return return
case <-t.C: case <-t.C:
m.lk.Lock() m.lk.Lock()
......
...@@ -7,12 +7,10 @@ ...@@ -7,12 +7,10 @@
package httputil package httputil
import ( import (
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"runtime"
"testing" "testing"
"time" "time"
) )
...@@ -112,10 +110,6 @@ func TestReverseProxyQuery(t *testing.T) { ...@@ -112,10 +110,6 @@ func TestReverseProxyQuery(t *testing.T) {
} }
func TestReverseProxyFlushInterval(t *testing.T) { func TestReverseProxyFlushInterval(t *testing.T) {
if testing.Short() {
return
}
const expected = "hi" const expected = "hi"
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(expected)) w.Write([]byte(expected))
...@@ -130,38 +124,28 @@ func TestReverseProxyFlushInterval(t *testing.T) { ...@@ -130,38 +124,28 @@ func TestReverseProxyFlushInterval(t *testing.T) {
proxyHandler := NewSingleHostReverseProxy(backendURL) proxyHandler := NewSingleHostReverseProxy(backendURL)
proxyHandler.FlushInterval = time.Microsecond proxyHandler.FlushInterval = time.Microsecond
dstChan := make(chan io.Writer, 1) done := make(chan bool)
beforeCopyResponse = func(dst io.Writer) { dstChan <- dst } onExitFlushLoop = func() { done <- true }
defer func() { beforeCopyResponse = nil }() defer func() { onExitFlushLoop = nil }()
frontend := httptest.NewServer(proxyHandler) frontend := httptest.NewServer(proxyHandler)
defer frontend.Close() defer frontend.Close()
initGoroutines := runtime.NumGoroutine() req, _ := http.NewRequest("GET", frontend.URL, nil)
for i := 0; i < 100; i++ { req.Close = true
req, _ := http.NewRequest("GET", frontend.URL, nil) res, err := http.DefaultClient.Do(req)
req.Close = true if err != nil {
res, err := http.DefaultClient.Do(req) t.Fatalf("Get: %v", err)
if err != nil { }
t.Fatalf("Get: %v", err) defer res.Body.Close()
} if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected {
if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected { t.Errorf("got body %q; expected %q", bodyBytes, expected)
t.Errorf("got body %q; expected %q", bodyBytes, expected)
}
select {
case dst := <-dstChan:
if _, ok := dst.(*maxLatencyWriter); !ok {
t.Errorf("got writer %T; expected %T", dst, &maxLatencyWriter{})
}
default:
t.Error("maxLatencyWriter Write() was never called")
}
res.Body.Close()
} }
// Allow up to 50 additional goroutines over 100 requests.
if delta := runtime.NumGoroutine() - initGoroutines; delta > 50 { select {
t.Errorf("grew %d goroutines; leak?", delta) case <-done:
// OK
case <-time.After(5 * time.Second):
t.Error("maxLatencyWriter flushLoop() never exited")
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment