func TestHTTPStopTimeoutMissed(t *testing.T) { t.Parallel() klock := clock.NewMock() const count = 10000 hello := []byte("hello") finOkHandler := make(chan struct{}) unblockOkHandler := make(chan struct{}) okHandler := func(w http.ResponseWriter, r *http.Request) { defer close(finOkHandler) w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count)) w.WriteHeader(200) for i := 0; i < count/2; i++ { w.Write(hello) } <-unblockOkHandler for i := 0; i < count/2; i++ { w.Write(hello) } } listener, err := net.Listen("tcp", "127.0.0.1:0") ensure.Nil(t, err) server := &http.Server{Handler: http.HandlerFunc(okHandler)} transport := &http.Transport{} client := &http.Client{Transport: transport} down := &httpdown.HTTP{ StopTimeout: time.Minute, Clock: klock, } s := down.Serve(server, listener) res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String())) ensure.Nil(t, err) finStop := make(chan struct{}) go func() { defer close(finStop) ensure.Nil(t, s.Stop()) }() klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After klock.Add(down.StopTimeout) _, err = ioutil.ReadAll(res.Body) ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$")) ensure.Nil(t, res.Body.Close()) close(unblockOkHandler) <-finOkHandler <-finStop }
func TestHTTPKillTimeoutMissed(t *testing.T) { t.Parallel() klock := clock.NewMock() statsDone := make(chan struct{}, 1) hc := &stats.HookClient{ BumpSumHook: func(key string, val float64) { if key == "kill.timeout" && val == 1 { statsDone <- struct{}{} } }, } const count = 10000 hello := []byte("hello") finOkHandler := make(chan struct{}) unblockOkHandler := make(chan struct{}) okHandler := func(w http.ResponseWriter, r *http.Request) { defer close(finOkHandler) w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count)) w.WriteHeader(200) for i := 0; i < count/2; i++ { w.Write(hello) } <-unblockOkHandler for i := 0; i < count/2; i++ { w.Write(hello) } } listener, err := net.Listen("tcp", "127.0.0.1:0") ensure.Nil(t, err) unblockConnClose := make(chan chan struct{}, 1) listener = &closeErrConnListener{ Listener: listener, unblockClose: unblockConnClose, } server := &http.Server{Handler: http.HandlerFunc(okHandler)} transport := &http.Transport{} client := &http.Client{Transport: transport} down := &httpdown.HTTP{ StopTimeout: time.Minute, KillTimeout: time.Minute, Stats: hc, Clock: klock, } s := down.Serve(server, listener) res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String())) ensure.Nil(t, err) // Start the Stop process. finStop := make(chan struct{}) go func() { defer close(finStop) ensure.Nil(t, s.Stop()) }() klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After klock.Add(down.StopTimeout) // trigger stop timeout klock.Wait(clock.Calls{After: 2}) // wait for Kill to call After klock.Add(down.KillTimeout) // trigger kill timeout // We hit both the StopTimeout & the KillTimeout. <-finStop // Then we unblock the Close, so we get an unexpected EOF since we close // before we finish writing the response. connCloseDone := make(chan struct{}) unblockConnClose <- connCloseDone <-connCloseDone close(unblockConnClose) // Then we unblock the handler which tries to write the rest of the data. close(unblockOkHandler) _, err = ioutil.ReadAll(res.Body) ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$")) ensure.Nil(t, res.Body.Close()) <-finOkHandler <-statsDone }