From a3aa414ff35294f539ca3d900f20dc66ead627c5 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 21 Dec 2016 20:44:07 +0100 Subject: [PATCH] Fixed HTTP/2 support for the proxy middleware (#1300) * Fixed HTTP/2 support for the proxy middleware http.Transport instances whose TLSClientConfig, Dial, or DialTLS field is non-nil will be configured without HTTP/2 support by default. This commit adds the proper calls to http2.ConfigureTransport() everywhere a http.Transport is created and thus fixes HTTP/2 in the proxy middleware whenever insecure_skip_verify or keepalive is provided. * Added HTTP/2 support check to TestReverseProxyInsecureSkipVerify --- caddyhttp/proxy/proxy_test.go | 19 ++++++++++++++++++- caddyhttp/proxy/reverseproxy.go | 14 ++++++++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/caddyhttp/proxy/proxy_test.go b/caddyhttp/proxy/proxy_test.go index d8c1b0cf..6359596c 100644 --- a/caddyhttp/proxy/proxy_test.go +++ b/caddyhttp/proxy/proxy_test.go @@ -3,6 +3,7 @@ package proxy import ( "bufio" "bytes" + "crypto/tls" "fmt" "io" "io/ioutil" @@ -26,6 +27,17 @@ import ( "golang.org/x/net/websocket" ) +// This is a simple wrapper around httptest.NewTLSServer() +// which forcefully enables (among others) HTTP/2 support. +// The httptest package only supports HTTP/1.1 by default. +func newTLSServer(handler http.Handler) *httptest.Server { + ts := httptest.NewUnstartedServer(handler) + ts.TLS = new(tls.Config) + ts.TLS.NextProtos = []string{"h2"} + ts.StartTLS() + return ts +} + func TestReverseProxy(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stderr) @@ -69,8 +81,10 @@ func TestReverseProxyInsecureSkipVerify(t *testing.T) { defer log.SetOutput(os.Stderr) var requestReceived bool - backend := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var requestWasHTTP2 bool + backend := newTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requestReceived = true + requestWasHTTP2 = r.ProtoAtLeast(2, 0) w.Write([]byte("Hello, client")) })) defer backend.Close() @@ -90,6 +104,9 @@ func TestReverseProxyInsecureSkipVerify(t *testing.T) { if !requestReceived { t.Error("Even with insecure HTTPS, expected backend to receive request, but it didn't") } + if !requestWasHTTP2 { + t.Error("Even with insecure HTTPS, expected proxy to use HTTP/2") + } } func TestWebSocketReverseProxyNonHijackerPanic(t *testing.T) { diff --git a/caddyhttp/proxy/reverseproxy.go b/caddyhttp/proxy/reverseproxy.go index 03b63786..cfb466c7 100644 --- a/caddyhttp/proxy/reverseproxy.go +++ b/caddyhttp/proxy/reverseproxy.go @@ -22,6 +22,8 @@ import ( "sync" "time" + "golang.org/x/net/http2" + "github.com/mholt/caddy/caddyhttp/httpserver" ) @@ -132,7 +134,7 @@ func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int) * // if keepalive is equal to the default, // just use default transport, to avoid creating // a brand new transport - rp.Transport = &http.Transport{ + transport := &http.Transport{ Proxy: http.ProxyFromEnvironment, Dial: (&net.Dialer{ Timeout: 30 * time.Second, @@ -142,10 +144,12 @@ func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int) * ExpectContinueTimeout: 1 * time.Second, } if keepalive == 0 { - rp.Transport.(*http.Transport).DisableKeepAlives = true + transport.DisableKeepAlives = true } else { - rp.Transport.(*http.Transport).MaxIdleConnsPerHost = keepalive + transport.MaxIdleConnsPerHost = keepalive } + http2.ConfigureTransport(transport) + rp.Transport = transport } return rp } @@ -155,7 +159,7 @@ func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int) * // since this transport skips verification. func (rp *ReverseProxy) UseInsecureTransport() { if rp.Transport == nil { - rp.Transport = &http.Transport{ + transport := &http.Transport{ Proxy: http.ProxyFromEnvironment, Dial: (&net.Dialer{ Timeout: 30 * time.Second, @@ -164,6 +168,8 @@ func (rp *ReverseProxy) UseInsecureTransport() { TLSHandshakeTimeout: 10 * time.Second, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } + http2.ConfigureTransport(transport) + rp.Transport = transport } else if transport, ok := rp.Transport.(*http.Transport); ok { transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} }