diff options
author | Tom Barrett <tom@tombarrett.xyz> | 2023-11-01 17:57:48 +0100 |
---|---|---|
committer | Tom Barrett <tom@tombarrett.xyz> | 2023-11-01 18:11:33 +0100 |
commit | 240c3d1338415e5d82ef7ca0e52c4284be6441bd (patch) | |
tree | 4b0ee5d208c2cdffa78d65f1b0abe0ec85f15652 /modules/caddyhttp/app.go | |
parent | 73e78ab226f21e6c6c68961af88c4ab9c746f4f4 (diff) | |
parent | 0e204b730aa2b1fa0835336b1117eff8c420f713 (diff) |
Diffstat (limited to 'modules/caddyhttp/app.go')
-rw-r--r-- | modules/caddyhttp/app.go | 96 |
1 files changed, 79 insertions, 17 deletions
diff --git a/modules/caddyhttp/app.go b/modules/caddyhttp/app.go index 36a4011..457a5f4 100644 --- a/modules/caddyhttp/app.go +++ b/modules/caddyhttp/app.go @@ -20,16 +20,19 @@ import ( "fmt" "net" "net/http" + "runtime" "strconv" + "strings" "sync" "time" - "github.com/caddyserver/caddy/v2" - "github.com/caddyserver/caddy/v2/modules/caddyevents" - "github.com/caddyserver/caddy/v2/modules/caddytls" "go.uber.org/zap" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" + + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/modules/caddyevents" + "github.com/caddyserver/caddy/v2/modules/caddytls" ) func init() { @@ -232,6 +235,11 @@ func (app *App) Provision(ctx caddy.Context) error { srv.trustedProxies = val.(IPRangeSource) } + // set the default client IP header to read from + if srv.ClientIPHeaders == nil { + srv.ClientIPHeaders = []string{"X-Forwarded-For"} + } + // process each listener address for i := range srv.Listen { lnOut, err := repl.ReplaceOrErr(srv.Listen[i], true, true) @@ -288,11 +296,19 @@ func (app *App) Provision(ctx caddy.Context) error { if srv.Errors != nil { err := srv.Errors.Routes.Provision(ctx) if err != nil { - return fmt.Errorf("server %s: setting up server error handling routes: %v", srvName, err) + return fmt.Errorf("server %s: setting up error handling routes: %v", srvName, err) } srv.errorHandlerChain = srv.Errors.Routes.Compile(errorEmptyHandler) } + // provision the named routes (they get compiled at runtime) + for name, route := range srv.NamedRoutes { + err := route.Provision(ctx, srv.Metrics) + if err != nil { + return fmt.Errorf("server %s: setting up named route '%s' handlers: %v", name, srvName, err) + } + } + // prepare the TLS connection policies err = srv.TLSConnPolicies.Provision(ctx) if err != nil { @@ -312,9 +328,15 @@ func (app *App) Provision(ctx caddy.Context) error { // Validate ensures the app's configuration is valid. func (app *App) Validate() error { + isGo120 := strings.Contains(runtime.Version(), "go1.20") + // each server must use distinct listener addresses lnAddrs := make(map[string]string) for srvName, srv := range app.Servers { + if isGo120 && srv.EnableFullDuplex { + app.logger.Warn("enable_full_duplex is not supported in Go 1.20, use a build made with Go 1.21 or later", zap.String("server", srvName)) + } + for _, addr := range srv.Listen { listenAddr, err := caddy.ParseNetworkAddress(addr) if err != nil { @@ -352,6 +374,14 @@ func (app *App) Start() error { MaxHeaderBytes: srv.MaxHeaderBytes, Handler: srv, ErrorLog: serverLogger, + ConnContext: func(ctx context.Context, c net.Conn) context.Context { + return context.WithValue(ctx, ConnCtxKey, c) + }, + } + h2server := &http2.Server{ + NewWriteScheduler: func() http2.WriteScheduler { + return http2.NewPriorityWriteScheduler(nil) + }, } // disable HTTP/2, which we enabled by default during provisioning @@ -373,6 +403,9 @@ func (app *App) Start() error { } } } + } else { + //nolint:errcheck + http2.ConfigureServer(srv.server, h2server) } // this TLS config is used by the std lib to choose the actual TLS config for connections @@ -382,9 +415,6 @@ func (app *App) Start() error { // enable H2C if configured if srv.protocol("h2c") { - h2server := &http2.Server{ - IdleTimeout: time.Duration(srv.IdleTimeout), - } srv.server.Handler = h2c.NewHandler(srv, h2server) } @@ -451,6 +481,17 @@ func (app *App) Start() error { ln = srv.listenerWrappers[i].WrapListener(ln) } + // handle http2 if use tls listener wrapper + if useTLS { + http2lnWrapper := &http2Listener{ + Listener: ln, + server: srv.server, + h2server: h2server, + } + srv.h2listeners = append(srv.h2listeners, http2lnWrapper) + ln = http2lnWrapper + } + // if binding to port 0, the OS chooses a port for us; // but the user won't know the port unless we print it if !listenAddr.IsUnixNetwork() && listenAddr.StartPort == 0 && listenAddr.EndPort == 0 { @@ -517,7 +558,7 @@ func (app *App) Stop() error { // honor scheduled/delayed shutdown time if delay { - app.logger.Debug("shutdown scheduled", + app.logger.Info("shutdown scheduled", zap.Duration("delay_duration", time.Duration(app.ShutdownDelay)), zap.Time("time", scheduledTime)) time.Sleep(time.Duration(app.ShutdownDelay)) @@ -528,9 +569,9 @@ func (app *App) Stop() error { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, time.Duration(app.GracePeriod)) defer cancel() - app.logger.Debug("servers shutting down; grace period initiated", zap.Duration("duration", time.Duration(app.GracePeriod))) + app.logger.Info("servers shutting down; grace period initiated", zap.Duration("duration", time.Duration(app.GracePeriod))) } else { - app.logger.Debug("servers shutting down with eternal grace period") + app.logger.Info("servers shutting down with eternal grace period") } // goroutines aren't guaranteed to be scheduled right away, @@ -562,6 +603,21 @@ func (app *App) Stop() error { return } + // First close h3server then close listeners unlike stdlib for several reasons: + // 1, udp has only a single socket, once closed, no more data can be read and + // written. In contrast, closing tcp listeners won't affect established connections. + // This have something to do with graceful shutdown when upstream implements it. + // 2, h3server will only close listeners it's registered (quic listeners). Closing + // listener first and these listeners maybe unregistered thus won't be closed. caddy + // distinguishes quic-listener and underlying datagram sockets. + + // TODO: CloseGracefully, once implemented upstream (see https://github.com/quic-go/quic-go/issues/2103) + if err := server.h3server.Close(); err != nil { + app.logger.Error("HTTP/3 server shutdown", + zap.Error(err), + zap.Strings("addresses", server.Listen)) + } + // TODO: we have to manually close our listeners because quic-go won't // close listeners it didn't create along with the server itself... // see https://github.com/quic-go/quic-go/issues/3560 @@ -572,20 +628,26 @@ func (app *App) Stop() error { zap.String("address", el.LocalAddr().String())) } } + } + stopH2Listener := func(server *Server) { + defer finishedShutdown.Done() + startedShutdown.Done() - // TODO: CloseGracefully, once implemented upstream (see https://github.com/quic-go/quic-go/issues/2103) - if err := server.h3server.Close(); err != nil { - app.logger.Error("HTTP/3 server shutdown", - zap.Error(err), - zap.Strings("addresses", server.Listen)) + for i, s := range server.h2listeners { + if err := s.Shutdown(ctx); err != nil { + app.logger.Error("http2 listener shutdown", + zap.Error(err), + zap.Int("index", i)) + } } } for _, server := range app.Servers { - startedShutdown.Add(2) - finishedShutdown.Add(2) + startedShutdown.Add(3) + finishedShutdown.Add(3) go stopServer(server) go stopH3Server(server) + go stopH2Listener(server) } // block until all the goroutines have been run by the scheduler; |