From 864ce5f946e1923900ba6fdb01921987451e7982 Mon Sep 17 00:00:00 2001 From: Wolfgang Ebner Date: Tue, 16 Jun 2015 20:39:42 +0200 Subject: [PATCH 1/3] proxy: handle canceled proxy request gracefully when a client of the proxy server cancels a request the proxy should not set the endpoint state to unavailable --- proxy/reverse.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/proxy/reverse.go b/proxy/reverse.go index 0db5bd111..a707a38b1 100644 --- a/proxy/reverse.go +++ b/proxy/reverse.go @@ -90,6 +90,7 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request return } + requestCanceled := false completeCh := make(chan bool, 1) closeNotifier, ok := rw.(http.CloseNotifier) if ok { @@ -98,6 +99,8 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request case <-closeNotifier.CloseNotify(): tp, ok := p.transport.(*http.Transport) if ok { + requestCanceled = true + log.Printf("proxy: request from %v canceled", clientreq.RemoteAddr) tp.CancelRequest(proxyreq) } case <-completeCh: @@ -118,6 +121,9 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request redirectRequest(proxyreq, ep.URL) res, err = p.transport.RoundTrip(proxyreq) + if requestCanceled { + return + } if err != nil { log.Printf("proxy: failed to direct request to %s: %v", ep.URL.String(), err) ep.Failed() From 5e31854afd3da752f68736d8e7b798c72bbe4a00 Mon Sep 17 00:00:00 2001 From: Wolfgang Ebner Date: Wed, 17 Jun 2015 10:15:00 +0200 Subject: [PATCH 2/3] proxy: use atomic operations for requestCanceled flag --- proxy/reverse.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proxy/reverse.go b/proxy/reverse.go index a707a38b1..3a2bcf297 100644 --- a/proxy/reverse.go +++ b/proxy/reverse.go @@ -24,6 +24,7 @@ import ( "net/http" "net/url" "strings" + "sync/atomic" "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes" ) @@ -90,7 +91,7 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request return } - requestCanceled := false + var requestClosed int32 completeCh := make(chan bool, 1) closeNotifier, ok := rw.(http.CloseNotifier) if ok { @@ -99,7 +100,7 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request case <-closeNotifier.CloseNotify(): tp, ok := p.transport.(*http.Transport) if ok { - requestCanceled = true + atomic.StoreInt32(&requestClosed, 1) log.Printf("proxy: request from %v canceled", clientreq.RemoteAddr) tp.CancelRequest(proxyreq) } @@ -121,7 +122,7 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request redirectRequest(proxyreq, ep.URL) res, err = p.transport.RoundTrip(proxyreq) - if requestCanceled { + if atomic.LoadInt32(&requestClosed) == 1 { return } if err != nil { From a6e61864773481e10da5fa882ce3e864f9c9d28d Mon Sep 17 00:00:00 2001 From: Wolfgang Ebner Date: Fri, 19 Jun 2015 08:45:45 +0200 Subject: [PATCH 3/3] proxy: always set requestClosed flag when client closes the connection prematurely --- proxy/reverse.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proxy/reverse.go b/proxy/reverse.go index 3a2bcf297..0d6822e52 100644 --- a/proxy/reverse.go +++ b/proxy/reverse.go @@ -98,10 +98,11 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request go func() { select { case <-closeNotifier.CloseNotify(): + atomic.StoreInt32(&requestClosed, 1) + log.Printf("proxy: client %v closed request prematurely", clientreq.RemoteAddr) + tp, ok := p.transport.(*http.Transport) if ok { - atomic.StoreInt32(&requestClosed, 1) - log.Printf("proxy: request from %v canceled", clientreq.RemoteAddr) tp.CancelRequest(proxyreq) } case <-completeCh: