caddyhttp: Enable HTTP/3 by default (#4707)

This commit is contained in:
Matt Holt
2022-08-15 12:01:58 -06:00
committed by GitHub
parent e2a5e2293a
commit c79c08627d
8 changed files with 210 additions and 191 deletions

View File

@ -25,7 +25,6 @@ import (
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddytls"
"github.com/lucas-clemente/quic-go/http3"
"go.uber.org/zap"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
@ -185,6 +184,17 @@ func (app *App) Provision(ctx caddy.Context) error {
srv.accessLogger = app.logger.Named("log.access")
}
// the Go standard library does not let us serve only HTTP/2 using
// http.Server; we would probably need to write our own server
if !srv.protocol("h1") && (srv.protocol("h2") || srv.protocol("h2c")) {
return fmt.Errorf("server %s: cannot enable HTTP/2 or H2C without enabling HTTP/1.1; add h1 to protocols or remove h2/h2c", srvName)
}
// if no protocols configured explicitly, enable all except h2c
if len(srv.Protocols) == 0 {
srv.Protocols = []string{"h1", "h2", "h3"}
}
// if not explicitly configured by the user, disallow TLS
// client auth bypass (domain fronting) which could
// otherwise be exploited by sending an unprotected SNI
@ -196,8 +206,7 @@ func (app *App) Provision(ctx caddy.Context) error {
// based on hostname
if srv.StrictSNIHost == nil && srv.hasTLSClientAuth() {
app.logger.Warn("enabling strict SNI-Host enforcement because TLS client auth is configured",
zap.String("server_id", srvName),
)
zap.String("server_id", srvName))
trueBool := true
srv.StrictSNIHost = &trueBool
}
@ -206,8 +215,7 @@ func (app *App) Provision(ctx caddy.Context) error {
for i := range srv.Listen {
lnOut, err := repl.ReplaceOrErr(srv.Listen[i], true, true)
if err != nil {
return fmt.Errorf("server %s, listener %d: %v",
srvName, i, err)
return fmt.Errorf("server %s, listener %d: %v", srvName, i, err)
}
srv.Listen[i] = lnOut
}
@ -324,10 +332,34 @@ func (app *App) Start() error {
Handler: srv,
ErrorLog: serverLogger,
}
// disable HTTP/2, which we enabled by default during provisioning
if !srv.protocol("h2") {
srv.server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
for _, cp := range srv.TLSConnPolicies {
// the TLSConfig was already provisioned, so... manually remove it
for i, np := range cp.TLSConfig.NextProtos {
if np == "h2" {
cp.TLSConfig.NextProtos = append(cp.TLSConfig.NextProtos[:i], cp.TLSConfig.NextProtos[i+1:]...)
break
}
}
// remove it from the parent connection policy too, just to keep things tidy
for i, alpn := range cp.ALPN {
if alpn == "h2" {
cp.ALPN = append(cp.ALPN[:i], cp.ALPN[i+1:]...)
break
}
}
}
}
// this TLS config is used by the std lib to choose the actual TLS config for connections
// by looking through the connection policies to find the first one that matches
tlsCfg := srv.TLSConnPolicies.TLSConfig(app.ctx)
// enable h2c if configured
if srv.AllowH2C {
// enable H2C if configured
if srv.protocol("h2c") {
h2server := &http2.Server{
IdleTimeout: time.Duration(srv.IdleTimeout),
}
@ -362,27 +394,15 @@ func (app *App) Start() error {
// enable TLS if there is a policy and if this is not the HTTP port
useTLS := len(srv.TLSConnPolicies) > 0 && int(listenAddr.StartPort+portOffset) != app.httpPort()
if useTLS {
// create TLS listener
// create TLS listener - this enables and terminates TLS
ln = tls.NewListener(ln, tlsCfg)
// TODO: HTTP/3 support is experimental for now
if srv.ExperimentalHTTP3 {
if srv.h3server == nil {
srv.h3server = &http3.Server{
Handler: srv,
TLSConfig: tlsCfg,
MaxHeaderBytes: srv.MaxHeaderBytes,
}
// enable HTTP/3 if configured
if srv.protocol("h3") {
app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport))
if err := srv.serveHTTP3(hostport, tlsCfg); err != nil {
return err
}
app.logger.Info("enabling experimental HTTP/3 listener", zap.String("addr", hostport))
h3ln, err := caddy.ListenQUIC(hostport, tlsCfg)
if err != nil {
return fmt.Errorf("getting HTTP/3 QUIC listener: %v", err)
}
//nolint:errcheck
go srv.h3server.ServeListener(h3ln)
}
}
@ -402,16 +422,22 @@ func (app *App) Start() error {
app.logger.Debug("starting server loop",
zap.String("address", ln.Addr().String()),
zap.Bool("http3", srv.ExperimentalHTTP3),
zap.Bool("tls", useTLS),
)
zap.Bool("http3", srv.h3server != nil))
srv.listeners = append(srv.listeners, ln)
//nolint:errcheck
go srv.server.Serve(ln)
// enable HTTP/1 if configured
if srv.protocol("h1") {
//nolint:errcheck
go srv.server.Serve(ln)
}
}
}
srv.logger.Info("server running",
zap.String("name", srvName),
zap.Strings("protocols", srv.Protocols))
}
// finish automatic HTTPS by finally beginning