Browse Source

etcdserver/etcdhttp: cancel long requests on conn close

Blake Mizerany 11 years ago
parent
commit
735647e6a3
1 changed files with 21 additions and 4 deletions
  1. 21 4
      etcdserver2/etcdhttp/http.go

+ 21 - 4
etcdserver2/etcdhttp/http.go

@@ -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)
+}