From ca3c0df1f8e07337ba4048b191bf905118ebe251 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 8 Oct 2020 20:32:50 +0000 Subject: net/http/httputil: flush ReverseProxy immediately if Content-Length is -1 Finish up a prior TODO by making ReverseProxy flush immediately if Content-Length is -1, which is a case that can occur if for example we have a streamed response, or chunked encoding, or when the body's length wasn't known. Fixes #41642 Change-Id: I30babaaf3e14837b99e3ecdc562a0a0e50c579bf GitHub-Last-Rev: efc019a9fe361082d40bee77317018c3b80451a3 GitHub-Pull-Request: golang/go#41858 Reviewed-on: https://go-review.googlesource.com/c/go/+/260637 Run-TryBot: Emmanuel Odeke TryBot-Result: Go Bot Trust: Ian Lance Taylor Trust: Emmanuel Odeke Reviewed-by: Emmanuel Odeke --- src/net/http/httputil/reverseproxy.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src/net/http/httputil/reverseproxy.go') diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go index 3f48fab544..46e5f68a84 100644 --- a/src/net/http/httputil/reverseproxy.go +++ b/src/net/http/httputil/reverseproxy.go @@ -58,9 +58,9 @@ type ReverseProxy struct { // A negative value means to flush immediately // after each write to the client. // The FlushInterval is ignored when ReverseProxy - // recognizes a response as a streaming response; - // for such responses, writes are flushed to the client - // immediately. + // recognizes a response as a streaming response, or + // if its ContentLength is -1; for such responses, writes + // are flushed to the client immediately. FlushInterval time.Duration // ErrorLog specifies an optional logger for errors @@ -325,7 +325,7 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(res.StatusCode) - err = p.copyResponse(rw, res.Body, p.flushInterval(req, res)) + err = p.copyResponse(rw, res.Body, p.flushInterval(res)) if err != nil { defer res.Body.Close() // Since we're streaming the response, if we run into an error all we can do @@ -397,7 +397,7 @@ func removeConnectionHeaders(h http.Header) { // flushInterval returns the p.FlushInterval value, conditionally // overriding its value for a specific request/response. -func (p *ReverseProxy) flushInterval(req *http.Request, res *http.Response) time.Duration { +func (p *ReverseProxy) flushInterval(res *http.Response) time.Duration { resCT := res.Header.Get("Content-Type") // For Server-Sent Events responses, flush immediately. @@ -406,7 +406,11 @@ func (p *ReverseProxy) flushInterval(req *http.Request, res *http.Response) time return -1 // negative means immediately } - // TODO: more specific cases? e.g. res.ContentLength == -1? + // We might have the case of streaming for which Content-Length might be unset. + if res.ContentLength == -1 { + return -1 + } + return p.FlushInterval } -- cgit v1.3 From 9c017ff30dd21bbdcdb11f39458d3944db530d7e Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Sat, 26 Sep 2020 13:21:41 +0800 Subject: net/http/httputil: copy response header back to http.ResponseWriter in ReverseProxy.handleUpgradeResponse Fixes: #41634 Change-Id: Ib78cc37a4d2ca0753d567eafb616238e4103484e Reviewed-on: https://go-review.googlesource.com/c/go/+/257777 Reviewed-by: Damien Neil Trust: Damien Neil Trust: Brad Fitzpatrick Run-TryBot: Damien Neil TryBot-Result: Go Bot --- src/net/http/httputil/reverseproxy.go | 6 ++++-- src/net/http/httputil/reverseproxy_test.go | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/net/http/httputil/reverseproxy.go') diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go index 46e5f68a84..4e369580ea 100644 --- a/src/net/http/httputil/reverseproxy.go +++ b/src/net/http/httputil/reverseproxy.go @@ -549,8 +549,6 @@ func (p *ReverseProxy) handleUpgradeResponse(rw http.ResponseWriter, req *http.R return } - copyHeader(res.Header, rw.Header()) - hj, ok := rw.(http.Hijacker) if !ok { p.getErrorHandler()(rw, req, fmt.Errorf("can't switch protocols using non-Hijacker ResponseWriter type %T", rw)) @@ -581,6 +579,10 @@ func (p *ReverseProxy) handleUpgradeResponse(rw http.ResponseWriter, req *http.R return } defer conn.Close() + + copyHeader(rw.Header(), res.Header) + + res.Header = rw.Header() res.Body = nil // so res.Write only writes the headers; we have res.Body in backConn above if err := res.Write(brw); err != nil { p.getErrorHandler()(rw, req, fmt.Errorf("response write: %v", err)) diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go index ea786864d8..cc05d55d87 100644 --- a/src/net/http/httputil/reverseproxy_test.go +++ b/src/net/http/httputil/reverseproxy_test.go @@ -1157,6 +1157,9 @@ func TestReverseProxyWebSocket(t *testing.T) { handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Set("X-Header", "X-Value") rproxy.ServeHTTP(rw, req) + if got, want := rw.Header().Get("X-Modified"), "true"; got != want { + t.Errorf("response writer X-Modified header = %q; want %q", got, want) + } }) frontendProxy := httptest.NewServer(handler) -- cgit v1.3