From 41c7bd27b495ab9b5edca7afb0cf99d5e1b52944 Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Tue, 5 May 2020 12:33:21 -0600 Subject: httpserver: Add experimental H2C support (#3289) * reverse_proxy: Initial attempt at H2C transport/client support (#3218) I have not tested this yet * Experimentally enabling H2C server support (closes #3227) See also #3218 I have not tested this * reverseproxy: Clean up H2C transport a bit * caddyhttp: Update godoc for h2c server; clarify experimental status * caddyhttp: Fix trailers when recording responses (fixes #3236) * caddyhttp: Tweak h2c config settings and docs --- modules/caddyhttp/reverseproxy/httptransport.go | 36 ++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'modules/caddyhttp/reverseproxy/httptransport.go') diff --git a/modules/caddyhttp/reverseproxy/httptransport.go b/modules/caddyhttp/reverseproxy/httptransport.go index 4b5111c..f0fbbd6 100644 --- a/modules/caddyhttp/reverseproxy/httptransport.go +++ b/modules/caddyhttp/reverseproxy/httptransport.go @@ -82,11 +82,16 @@ type HTTPTransport struct { // The size of the read buffer in bytes. ReadBufferSize int `json:"read_buffer_size,omitempty"` - // The versions of HTTP to support. Default: ["1.1", "2"] + // The versions of HTTP to support. As a special case, "h2c" + // can be specified to use H2C (HTTP/2 over Cleartext) to the + // upstream (this feature is experimental and subject to + // change or removal). Default: ["1.1", "2"] Versions []string `json:"versions,omitempty"` // The pre-configured underlying HTTP transport. Transport *http.Transport `json:"-"` + + h2cTransport *http2.Transport } // CaddyModule returns the Caddy module information. @@ -110,6 +115,28 @@ func (h *HTTPTransport) Provision(ctx caddy.Context) error { } h.Transport = rt + // if h2c is enabled, configure its transport (std lib http.Transport + // does not "HTTP/2 over cleartext TCP") + if sliceContains(h.Versions, "h2c") { + // crafting our own http2.Transport doesn't allow us to utilize + // most of the customizations/preferences on the http.Transport, + // because, for some reason, only http2.ConfigureTransport() + // is allowed to set the unexported field that refers to a base + // http.Transport config; oh well + h2t := &http2.Transport{ + // kind of a hack, but for plaintext/H2C requests, pretend to dial TLS + DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) { + // TODO: no context, thus potentially wrong dial info + return net.Dial(network, addr) + }, + AllowHTTP: true, + } + if h.Compression != nil { + h2t.DisableCompression = !*h.Compression + } + h.h2cTransport = h2t + } + return nil } @@ -182,6 +209,13 @@ func (h *HTTPTransport) NewTransport(_ caddy.Context) (*http.Transport, error) { // RoundTrip implements http.RoundTripper. func (h *HTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) { h.SetScheme(req) + + // if H2C ("HTTP/2 over cleartext") is enabled and the upstream request is + // HTTP/2 without TLS, use the alternate H2C-capable transport instead + if req.ProtoMajor == 2 && req.URL.Scheme == "http" && h.h2cTransport != nil { + return h.h2cTransport.RoundTrip(req) + } + return h.Transport.RoundTrip(req) } -- cgit v1.2.3