diff options
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; | 
