]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.12] net/http/httputil: make ReverseProxy flush headers on FlushIn...
authorJordan Liggitt <liggitt@google.com>
Fri, 29 Mar 2019 03:37:54 +0000 (23:37 -0400)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 2 Apr 2019 03:09:03 +0000 (03:09 +0000)
A regression was introduced in CL 137335 (5440bfc) that caused FlushInterval
to not be honored until the first Write() call was encountered. This change
starts the flush timer as part of setting up the maxLatencyWriter.

Fixes #31144

Change-Id: I75325bd926652922219bd1457b2b00ac6d0d41b0
Reviewed-on: https://go-review.googlesource.com/c/go/+/170066
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
(cherry picked from commit 2cc347382f4df3fb40d8d81ec9331f0748b1c394)
Reviewed-on: https://go-review.googlesource.com/c/go/+/170137

src/net/http/httputil/reverseproxy.go
src/net/http/httputil/reverseproxy_test.go

index 4e10bf399711b5f54e9cb985f0049738db46a97b..4b165d65a6aff44f352d9852bd64dd69421fd2b2 100644 (file)
@@ -389,6 +389,11 @@ func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader, flushInterval
                                latency: flushInterval,
                        }
                        defer mlw.stop()
+
+                       // set up initial timer so headers get flushed even if body writes are delayed
+                       mlw.flushPending = true
+                       mlw.t = time.AfterFunc(flushInterval, mlw.delayedFlush)
+
                        dst = mlw
                }
        }
index 5edefa08e55a3c1a17bda4d4957b12dbc4bfeb91..367ba73ae2497919748186a3ec886a0f17b8eef7 100644 (file)
@@ -9,6 +9,7 @@ package httputil
 import (
        "bufio"
        "bytes"
+       "context"
        "errors"
        "fmt"
        "io"
@@ -317,6 +318,47 @@ func TestReverseProxyFlushInterval(t *testing.T) {
        }
 }
 
+func TestReverseProxyFlushIntervalHeaders(t *testing.T) {
+       const expected = "hi"
+       stopCh := make(chan struct{})
+       backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+               w.Header().Add("MyHeader", expected)
+               w.WriteHeader(200)
+               w.(http.Flusher).Flush()
+               <-stopCh
+       }))
+       defer backend.Close()
+       defer close(stopCh)
+
+       backendURL, err := url.Parse(backend.URL)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       proxyHandler := NewSingleHostReverseProxy(backendURL)
+       proxyHandler.FlushInterval = time.Microsecond
+
+       frontend := httptest.NewServer(proxyHandler)
+       defer frontend.Close()
+
+       req, _ := http.NewRequest("GET", frontend.URL, nil)
+       req.Close = true
+
+       ctx, cancel := context.WithTimeout(req.Context(), 10*time.Second)
+       defer cancel()
+       req = req.WithContext(ctx)
+
+       res, err := frontend.Client().Do(req)
+       if err != nil {
+               t.Fatalf("Get: %v", err)
+       }
+       defer res.Body.Close()
+
+       if res.Header.Get("MyHeader") != expected {
+               t.Errorf("got header %q; expected %q", res.Header.Get("MyHeader"), expected)
+       }
+}
+
 func TestReverseProxyCancelation(t *testing.T) {
        const backendResponse = "I am the backend"