etcdserver/etcdhttp: cancel long requests on conn close

release-2.0
Blake Mizerany 2014-08-27 14:57:29 -07:00 committed by Yicheng Qin
parent 8cffd75e00
commit 735647e6a3
1 changed files with 21 additions and 4 deletions

View File

@ -26,10 +26,8 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
timeout = DefaultTimeout
}
ctx, _ := context.WithTimeout(context.Background(), timeout)
// TODO(bmizerany): watch the closenotify chan in another goroutine can
// call cancel when it closes. be sure to watch ctx.Done() too so we
// don't leak a ton of these goroutines.
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
rr, err := parseRequest(r)
if err != nil {
@ -37,6 +35,21 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
// avoid spawing goroutines for requests that are short lived.
if canBlock(rr) {
// cancel the request and release resources associated with it if the
// client closes their connection before we get a response.
if nf, ok := w.(http.CloseNotifier); ok {
go func() {
select {
case <-nf.CloseNotify():
cancel()
case <-ctx.Done():
}
}()
}
}
resp, err := h.Server.Do(ctx, rr)
if err != nil {
// TODO(bmizerany): switch on store errors and etcdserver.ErrUnknownMethod
@ -78,3 +91,7 @@ func encodeResponse(ctx context.Context, w http.ResponseWriter, resp etcdserver.
return json.NewEncoder(w).Encode(ev)
}
func canBlock(r etcdserver.Request) bool {
return r.Method != "GET" || (r.Method == "GET" && r.Wait)
}