@marcel91200 / caddy Mirror · read-only / commits / 41c7bd27
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

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

Matt Holt committed May 5, 2020 at 12:33 UTC 41c7bd27b495ab9b5edca7afb0cf99d5e1b52944
4 files changed +63 -1
modules/caddyhttp/app.go
+10
@@ -27,6 +27,8 @@ import (
27 "github.com/caddyserver/caddy/v2/modules/caddytls"
28 "github.com/lucas-clemente/quic-go/http3"
29 "go.uber.org/zap"
30 + "golang.org/x/net/http2"
31 + "golang.org/x/net/http2/h2c"
32 )
33
34 func init() {
@@ -282,6 +284,14 @@ func (app *App) Start() error {
284 Handler: srv,
285 }
286
287 + // enable h2c if configured
288 + if srv.AllowH2C {
289 + h2server := &http2.Server{
290 + IdleTimeout: time.Duration(srv.IdleTimeout),
291 + }
292 + s.Handler = h2c.NewHandler(srv, h2server)
293 + }
294 +
295 for _, lnAddr := range srv.Listen {
296 listenAddr, err := caddy.ParseNetworkAddress(lnAddr)
297 if err != nil {
modules/caddyhttp/reverseproxy/caddyfile.go
+7
@@ -584,6 +584,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
584 // tls_trusted_ca_certs <cert_files...>
585 // keepalive [off|<duration>]
586 // keepalive_idle_conns <max_count>
587 +// versions <versions...>
588 // }
589 //
590 func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
@@ -701,6 +702,12 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
702 h.KeepAlive.MaxIdleConns = num
703 h.KeepAlive.MaxIdleConnsPerHost = num
704
705 + case "versions":
706 + h.Versions = d.RemainingArgs()
707 + if len(h.Versions) == 0 {
708 + return d.ArgErr()
709 + }
710 +
711 default:
712 return d.Errf("unrecognized subdirective %s", d.Val())
713 }
modules/caddyhttp/reverseproxy/httptransport.go
+35 -1
@@ -82,11 +82,16 @@ type HTTPTransport struct {
82 // The size of the read buffer in bytes.
83 ReadBufferSize int `json:"read_buffer_size,omitempty"`
84
85 - // The versions of HTTP to support. Default: ["1.1", "2"]
85 + // The versions of HTTP to support. As a special case, "h2c"
86 + // can be specified to use H2C (HTTP/2 over Cleartext) to the
87 + // upstream (this feature is experimental and subject to
88 + // change or removal). Default: ["1.1", "2"]
89 Versions []string `json:"versions,omitempty"`
90
91 // The pre-configured underlying HTTP transport.
92 Transport *http.Transport `json:"-"`
93 +
94 + h2cTransport *http2.Transport
95 }
96
97 // CaddyModule returns the Caddy module information.
@@ -110,6 +115,28 @@ func (h *HTTPTransport) Provision(ctx caddy.Context) error {
115 }
116 h.Transport = rt
117
118 + // if h2c is enabled, configure its transport (std lib http.Transport
119 + // does not "HTTP/2 over cleartext TCP")
120 + if sliceContains(h.Versions, "h2c") {
121 + // crafting our own http2.Transport doesn't allow us to utilize
122 + // most of the customizations/preferences on the http.Transport,
123 + // because, for some reason, only http2.ConfigureTransport()
124 + // is allowed to set the unexported field that refers to a base
125 + // http.Transport config; oh well
126 + h2t := &http2.Transport{
127 + // kind of a hack, but for plaintext/H2C requests, pretend to dial TLS
128 + DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) {
129 + // TODO: no context, thus potentially wrong dial info
130 + return net.Dial(network, addr)
131 + },
132 + AllowHTTP: true,
133 + }
134 + if h.Compression != nil {
135 + h2t.DisableCompression = !*h.Compression
136 + }
137 + h.h2cTransport = h2t
138 + }
139 +
140 return nil
141 }
142
@@ -182,6 +209,13 @@ func (h *HTTPTransport) NewTransport(_ caddy.Context) (*http.Transport, error) {
209 // RoundTrip implements http.RoundTripper.
210 func (h *HTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) {
211 h.SetScheme(req)
212 +
213 + // if H2C ("HTTP/2 over cleartext") is enabled and the upstream request is
214 + // HTTP/2 without TLS, use the alternate H2C-capable transport instead
215 + if req.ProtoMajor == 2 && req.URL.Scheme == "http" && h.h2cTransport != nil {
216 + return h.h2cTransport.RoundTrip(req)
217 + }
218 +
219 return h.Transport.RoundTrip(req)
220 }
221
modules/caddyhttp/server.go
+11
@@ -111,6 +111,17 @@ type Server struct {
111 // This field is not subject to compatibility promises.
112 ExperimentalHTTP3 bool `json:"experimental_http3,omitempty"`
113
114 + // Enables H2C ("Cleartext HTTP/2" or "H2 over TCP") support,
115 + // which will serve HTTP/2 over plaintext TCP connections if
116 + // a client support it. Because this is not implemented by the
117 + // Go standard library, using H2C is incompatible with most
118 + // of the other options for this server. Do not enable this
119 + // only to achieve maximum client compatibility. In practice,
120 + // very few clients implement H2C, and even fewer require it.
121 + // This setting applies only to unencrypted HTTP listeners.
122 + // ⚠️ Experimental feature; subject to change or removal.
123 + AllowH2C bool `json:"allow_h2c,omitempty"`
124 +
125 primaryHandlerChain Handler
126 errorHandlerChain Handler
127 listenerWrappers []caddy.ListenerWrapper