Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

caddyhttp: Enable HTTP/3 by default #4707

Merged
merged 8 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 2 additions & 17 deletions caddyconfig/httpcaddyfile/serveroptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ type serverOptions struct {
IdleTimeout caddy.Duration
MaxHeaderBytes int
AllowH2C bool
ExperimentalHTTP3 bool
StrictSNIHost *bool
ShouldLogCredentials bool
}
Expand Down Expand Up @@ -150,11 +149,9 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (interface{}, error
}
serverOpts.AllowH2C = true

// TODO: deprecated - remove soon!
case "experimental_http3":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.ExperimentalHTTP3 = true
caddy.Log().Named("caddyfile").Warn("the experimental_http3 option is deprecated and will be removed soon as HTTP/3 is now enabled by default and is no longer experimental")

case "strict_sni_host":
if d.NextArg() && d.Val() != "insecure_off" && d.Val() != "on" {
Expand Down Expand Up @@ -185,17 +182,6 @@ func applyServerOptions(
options map[string]interface{},
warnings *[]caddyconfig.Warning,
) error {
// If experimental HTTP/3 is enabled, enable it on each server.
// We already know there won't be a conflict with serverOptions because
// we validated earlier that "experimental_http3" cannot be set at the same
// time as "servers"
if enableH3, ok := options["experimental_http3"].(bool); ok && enableH3 {
*warnings = append(*warnings, caddyconfig.Warning{Message: "the 'experimental_http3' global option is deprecated, please use the 'servers > protocol > experimental_http3' option instead"})
for _, srv := range servers {
srv.ExperimentalHTTP3 = true
}
}

serverOpts, ok := options["servers"].([]serverOptions)
if !ok {
return nil
Expand Down Expand Up @@ -230,7 +216,6 @@ func applyServerOptions(
server.IdleTimeout = opts.IdleTimeout
server.MaxHeaderBytes = opts.MaxHeaderBytes
server.AllowH2C = opts.AllowH2C
server.ExperimentalHTTP3 = opts.ExperimentalHTTP3
server.StrictSNIHost = opts.StrictSNIHost
if opts.ShouldLogCredentials {
if server.Logs == nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
log_credentials
protocol {
allow_h2c
experimental_http3
strict_sni_host
}
}
Expand Down Expand Up @@ -61,7 +60,6 @@ foo.com {
"logs": {
"should_log_credentials": true
},
"experimental_http3": true,
"allow_h2c": true
}
}
Expand Down
18 changes: 15 additions & 3 deletions listeners.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,24 @@ func ListenPacket(network, addr string) (net.PacketConn, error) {

// ListenQUIC returns a quic.EarlyListener suitable for use in a Caddy module.
// Note that the context passed to Accept is currently ignored, so using
// a context other than context.Background is meaningless.
func ListenQUIC(addr string, tlsConf *tls.Config) (quic.EarlyListener, error) {
// a context other than context.Background is meaningless. If activeRequests
// is not nil, it should represent the current load on the server.
func ListenQUIC(addr string, tlsConf *tls.Config, activeRequests *int64) (quic.EarlyListener, error) {
lnKey := "quic/" + addr

sharedEl, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
el, err := quic.ListenAddrEarly(addr, http3.ConfigureTLSConfig(tlsConf), &quic.Config{})
el, err := quic.ListenAddrEarly(addr, http3.ConfigureTLSConfig(tlsConf), &quic.Config{
AcceptToken: func(clientAddr net.Addr, token *quic.Token) bool {
mholt marked this conversation as resolved.
Show resolved Hide resolved
if token == nil {
return false
}
var highLoad bool
if activeRequests != nil {
highLoad = atomic.LoadInt64(activeRequests) > 1000 // TODO: make tunable
}
return highLoad
},
})
if err != nil {
return nil, err
}
Expand Down
43 changes: 18 additions & 25 deletions modules/caddyhttp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,30 +346,24 @@ func (app *App) Start() error {
tlsCfg := srv.TLSConnPolicies.TLSConfig(app.ctx)
ln = tls.NewListener(ln, tlsCfg)

/////////
// TODO: HTTP/3 support is experimental for now
if srv.ExperimentalHTTP3 {
mholt marked this conversation as resolved.
Show resolved Hide resolved
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)
}
h3srv := &http3.Server{
Server: &http.Server{
Addr: hostport,
Handler: srv,
TLSConfig: tlsCfg,
ErrorLog: serverLogger,
},
}
//nolint:errcheck
go h3srv.ServeListener(h3ln)
app.h3servers = append(app.h3servers, h3srv)
srv.h3server = h3srv
// create HTTP/3 listener
app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport))
h3ln, err := caddy.ListenQUIC(hostport, tlsCfg, &srv.activeRequests)
if err != nil {
return fmt.Errorf("starting HTTP/3 QUIC listener: %v", err)
}
/////////
h3srv := &http3.Server{
Server: &http.Server{
Addr: hostport,
Handler: srv,
TLSConfig: tlsCfg,
ErrorLog: serverLogger,
},
}
//nolint:errcheck
go h3srv.ServeListener(h3ln)
app.h3servers = append(app.h3servers, h3srv)
srv.h3server = h3srv
}

// finish wrapping listener where we left off before TLS
Expand All @@ -388,9 +382,8 @@ 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))

//nolint:errcheck
go s.Serve(ln)
Expand Down
13 changes: 10 additions & 3 deletions modules/caddyhttp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"net/url"
"runtime"
"strings"
"sync/atomic"
"time"

"github.com/caddyserver/caddy/v2"
Expand All @@ -36,6 +37,8 @@ import (

// Server describes an HTTP server.
type Server struct {
activeRequests int64 // accessed atomically

// Socket addresses to which to bind listeners. Accepts
// [network addresses](/docs/conventions#network-addresses)
// that may include port ranges. Listener addresses must
Expand Down Expand Up @@ -111,9 +114,9 @@ type Server struct {
// to a non-null, empty struct.
Logs *ServerLogConfig `json:"logs,omitempty"`

// Enable experimental HTTP/3 support. Note that HTTP/3 is not a
// finished standard and has extremely limited client support.
// This field is not subject to compatibility promises.
// DEPRECATED: WILL BE REMOVED SOON. HTTP/3 is enabled by default
// when possible. Please remove this from your configs.
// TODO: remove
ExperimentalHTTP3 bool `json:"experimental_http3,omitempty"`

// Enables H2C ("Cleartext HTTP/2" or "H2 over TCP") support,
Expand Down Expand Up @@ -146,6 +149,10 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", "Caddy")

if s.h3server != nil {
// keep track of active requests for QUIC transport purposes (See AcceptToken callback in quic.Config)
atomic.AddInt64(&s.activeRequests, 1)
defer atomic.AddInt64(&s.activeRequests, -1)

err := s.h3server.SetQuicHeaders(w.Header())
if err != nil {
s.logger.Error("setting HTTP/3 Alt-Svc header", zap.Error(err))
Expand Down