"io"
"io/ioutil"
"log"
+ "math/rand"
"net"
. "net/http"
"net/http/httptest"
}
}
+// See issues 8209 and 8414.
+func TestTimeoutHandlerRace(t *testing.T) {
+ defer afterTest(t)
+
+ delayHi := HandlerFunc(func(w ResponseWriter, r *Request) {
+ ms, _ := strconv.Atoi(r.URL.Path[1:])
+ if ms == 0 {
+ ms = 1
+ }
+ for i := 0; i < ms; i++ {
+ w.Write([]byte("hi"))
+ time.Sleep(time.Millisecond)
+ }
+ })
+
+ ts := httptest.NewServer(TimeoutHandler(delayHi, 20*time.Millisecond, ""))
+ defer ts.Close()
+
+ var wg sync.WaitGroup
+ gate := make(chan bool, 10)
+ n := 50
+ if testing.Short() {
+ n = 10
+ gate = make(chan bool, 3)
+ }
+ for i := 0; i < n; i++ {
+ gate <- true
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ defer func() { <-gate }()
+ res, err := Get(fmt.Sprintf("%s/%d", ts.URL, rand.Intn(50)))
+ if err == nil {
+ io.Copy(ioutil.Discard, res.Body)
+ res.Body.Close()
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+// See issues 8209 and 8414.
+func TestTimeoutHandlerRaceHeader(t *testing.T) {
+ defer afterTest(t)
+
+ delay204 := HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.WriteHeader(204)
+ })
+
+ ts := httptest.NewServer(TimeoutHandler(delay204, time.Nanosecond, ""))
+ defer ts.Close()
+
+ var wg sync.WaitGroup
+ gate := make(chan bool, 50)
+ n := 500
+ if testing.Short() {
+ n = 10
+ }
+ for i := 0; i < n; i++ {
+ gate <- true
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ defer func() { <-gate }()
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer res.Body.Close()
+ io.Copy(ioutil.Discard, res.Body)
+ }()
+ }
+ wg.Wait()
+}
+
// Verifies we don't path.Clean() on the wrong parts in redirects.
func TestRedirectMunging(t *testing.T) {
req, _ := NewRequest("GET", "http://example.com/", nil)
func (tw *timeoutWriter) Write(p []byte) (int, error) {
tw.mu.Lock()
- timedOut := tw.timedOut
- tw.mu.Unlock()
- if timedOut {
+ defer tw.mu.Unlock()
+ tw.wroteHeader = true // implicitly at least
+ if tw.timedOut {
return 0, ErrHandlerTimeout
}
return tw.w.Write(p)
func (tw *timeoutWriter) WriteHeader(code int) {
tw.mu.Lock()
+ defer tw.mu.Unlock()
if tw.timedOut || tw.wroteHeader {
- tw.mu.Unlock()
return
}
tw.wroteHeader = true
- tw.mu.Unlock()
tw.w.WriteHeader(code)
}