mirror of
https://github.com/caddyserver/caddy.git
synced 2026-08-27 04:07:28 +00:00
Architectural shift to using context for config and module state
This commit is contained in:
parent
ff5b4639d5
commit
1f0c061ce3
11 changed files with 313 additions and 359 deletions
|
|
@ -37,16 +37,20 @@ type App struct {
|
|||
Servers map[string]*Server `json:"servers"`
|
||||
|
||||
servers []*http.Server
|
||||
|
||||
ctx caddy2.Context
|
||||
}
|
||||
|
||||
// Provision sets up the app.
|
||||
func (hc *App) Provision() error {
|
||||
for _, srv := range hc.Servers {
|
||||
err := srv.Routes.Provision()
|
||||
func (app *App) Provision(ctx caddy2.Context) error {
|
||||
app.ctx = ctx
|
||||
|
||||
for _, srv := range app.Servers {
|
||||
err := srv.Routes.Provision(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up server routes: %v", err)
|
||||
}
|
||||
err = srv.Errors.Routes.Provision()
|
||||
err = srv.Errors.Routes.Provision(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up server error handling routes: %v", err)
|
||||
}
|
||||
|
|
@ -56,10 +60,10 @@ func (hc *App) Provision() error {
|
|||
}
|
||||
|
||||
// Validate ensures the app's configuration is valid.
|
||||
func (hc *App) Validate() error {
|
||||
func (app *App) Validate() error {
|
||||
// each server must use distinct listener addresses
|
||||
lnAddrs := make(map[string]string)
|
||||
for srvName, srv := range hc.Servers {
|
||||
for srvName, srv := range app.Servers {
|
||||
for _, addr := range srv.Listen {
|
||||
netw, expanded, err := parseListenAddr(addr)
|
||||
if err != nil {
|
||||
|
|
@ -78,13 +82,13 @@ func (hc *App) Validate() error {
|
|||
}
|
||||
|
||||
// Start runs the app. It sets up automatic HTTPS if enabled.
|
||||
func (hc *App) Start(handle caddy2.Handle) error {
|
||||
err := hc.automaticHTTPS(handle)
|
||||
func (app *App) Start() error {
|
||||
err := app.automaticHTTPS()
|
||||
if err != nil {
|
||||
return fmt.Errorf("enabling automatic HTTPS: %v", err)
|
||||
}
|
||||
|
||||
for srvName, srv := range hc.Servers {
|
||||
for srvName, srv := range app.Servers {
|
||||
s := &http.Server{
|
||||
ReadTimeout: time.Duration(srv.ReadTimeout),
|
||||
ReadHeaderTimeout: time.Duration(srv.ReadHeaderTimeout),
|
||||
|
|
@ -110,13 +114,13 @@ func (hc *App) Start(handle caddy2.Handle) error {
|
|||
}
|
||||
|
||||
// enable TLS
|
||||
httpPort := hc.HTTPPort
|
||||
httpPort := app.HTTPPort
|
||||
if httpPort == 0 {
|
||||
httpPort = DefaultHTTPPort
|
||||
}
|
||||
_, port, _ := net.SplitHostPort(addr)
|
||||
if len(srv.TLSConnPolicies) > 0 && port != strconv.Itoa(httpPort) {
|
||||
tlsCfg, err := srv.TLSConnPolicies.TLSConfig(handle)
|
||||
tlsCfg, err := srv.TLSConnPolicies.TLSConfig(app.ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s/%s: making TLS configuration: %v", network, addr, err)
|
||||
}
|
||||
|
|
@ -124,7 +128,7 @@ func (hc *App) Start(handle caddy2.Handle) error {
|
|||
}
|
||||
|
||||
go s.Serve(ln)
|
||||
hc.servers = append(hc.servers, s)
|
||||
app.servers = append(app.servers, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -133,14 +137,14 @@ func (hc *App) Start(handle caddy2.Handle) error {
|
|||
}
|
||||
|
||||
// Stop gracefully shuts down the HTTP server.
|
||||
func (hc *App) Stop() error {
|
||||
func (app *App) Stop() error {
|
||||
ctx := context.Background()
|
||||
if hc.GracePeriod > 0 {
|
||||
if app.GracePeriod > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Duration(hc.GracePeriod))
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Duration(app.GracePeriod))
|
||||
defer cancel()
|
||||
}
|
||||
for _, s := range hc.servers {
|
||||
for _, s := range app.servers {
|
||||
err := s.Shutdown(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -149,8 +153,8 @@ func (hc *App) Stop() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
|
||||
tlsAppIface, err := handle.App("tls")
|
||||
func (app *App) automaticHTTPS() error {
|
||||
tlsAppIface, err := app.ctx.App("tls")
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting tls app: %v", err)
|
||||
}
|
||||
|
|
@ -159,7 +163,7 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
|
|||
lnAddrMap := make(map[string]struct{})
|
||||
var redirRoutes RouteList
|
||||
|
||||
for srvName, srv := range hc.Servers {
|
||||
for srvName, srv := range app.Servers {
|
||||
srv.tlsApp = tlsApp
|
||||
|
||||
if srv.DisableAutoHTTPS {
|
||||
|
|
@ -209,7 +213,7 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("%s: invalid listener address: %v", srvName, addr)
|
||||
}
|
||||
httpRedirLnAddr := joinListenAddr(netw, host, strconv.Itoa(hc.HTTPPort))
|
||||
httpRedirLnAddr := joinListenAddr(netw, host, strconv.Itoa(app.HTTPPort))
|
||||
lnAddrMap[httpRedirLnAddr] = struct{}{}
|
||||
|
||||
if parts := strings.SplitN(port, "-", 2); len(parts) == 2 {
|
||||
|
|
@ -217,7 +221,7 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
|
|||
}
|
||||
redirTo := "https://{request.host}"
|
||||
|
||||
httpsPort := hc.HTTPSPort
|
||||
httpsPort := app.HTTPSPort
|
||||
if httpsPort == 0 {
|
||||
httpsPort = DefaultHTTPSPort
|
||||
}
|
||||
|
|
@ -253,13 +257,13 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
|
|||
continue
|
||||
}
|
||||
for _, a := range addrs {
|
||||
if hc.listenerTaken(netw, a) {
|
||||
if app.listenerTaken(netw, a) {
|
||||
continue mapLoop
|
||||
}
|
||||
}
|
||||
lnAddrs = append(lnAddrs, addr)
|
||||
}
|
||||
hc.Servers["auto_https_redirects"] = &Server{
|
||||
app.Servers["auto_https_redirects"] = &Server{
|
||||
Listen: lnAddrs,
|
||||
Routes: redirRoutes,
|
||||
DisableAutoHTTPS: true,
|
||||
|
|
@ -269,8 +273,8 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (hc *App) listenerTaken(network, address string) bool {
|
||||
for _, srv := range hc.Servers {
|
||||
func (app *App) listenerTaken(network, address string) bool {
|
||||
for _, srv := range app.Servers {
|
||||
for _, addr := range srv.Listen {
|
||||
netw, addrs, err := parseListenAddr(addr)
|
||||
if err != nil || netw != network {
|
||||
|
|
@ -491,3 +495,4 @@ const (
|
|||
|
||||
// Interface guards
|
||||
var _ HTTPInterfaces = middlewareResponseWriter{}
|
||||
var _ caddy2.App = (*App)(nil)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package reverseproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"bitbucket.org/lightcodelabs/caddy2"
|
||||
)
|
||||
|
||||
|
|
@ -11,36 +9,5 @@ func init() {
|
|||
caddy2.RegisterModule(caddy2.Module{
|
||||
Name: "http.responders.reverse_proxy",
|
||||
New: func() (interface{}, error) { return new(LoadBalanced), nil },
|
||||
OnLoad: func(instances []interface{}, _ interface{}) (interface{}, error) {
|
||||
// we don't need to do anything with prior state because healthcheckers are
|
||||
// cleaned up in OnUnload.
|
||||
s := &State{
|
||||
HealthCheckers: []*HealthChecker{},
|
||||
}
|
||||
|
||||
for _, i := range instances {
|
||||
lb := i.(*LoadBalanced)
|
||||
|
||||
err := NewLoadBalancedReverseProxy(lb, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
},
|
||||
OnUnload: func(state interface{}) error {
|
||||
s, ok := state.(*State)
|
||||
if !ok {
|
||||
return fmt.Errorf("proxy OnLoad: prior state not expected proxy.State type")
|
||||
}
|
||||
|
||||
// cleanup old healthcheckers
|
||||
for _, hc := range s.HealthCheckers {
|
||||
hc.Stop()
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ var (
|
|||
)
|
||||
|
||||
// NewLoadBalancedReverseProxy returns a collection of Upstreams that are to be loadbalanced.
|
||||
func NewLoadBalancedReverseProxy(lb *LoadBalanced, state *State) error {
|
||||
func NewLoadBalancedReverseProxy(lb *LoadBalanced, state *State, ctx caddy2.Context) error {
|
||||
// set defaults
|
||||
if lb.NoHealthyUpstreamsMessage == "" {
|
||||
lb.NoHealthyUpstreamsMessage = msgNoHealthyUpstreams
|
||||
|
|
@ -115,7 +115,7 @@ func NewLoadBalancedReverseProxy(lb *LoadBalanced, state *State) error {
|
|||
|
||||
if uc.CircuitBreaker != nil {
|
||||
if _, err := caddy2.GetModule(cbModule); err == nil {
|
||||
val, err := caddy2.LoadModule(cbModule, uc.CircuitBreaker)
|
||||
val, err := ctx.LoadModule(cbModule, uc.CircuitBreaker)
|
||||
if err == nil {
|
||||
cbv, ok := val.(CircuitBreaker)
|
||||
if ok {
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ type ServerRoute struct {
|
|||
type RouteList []ServerRoute
|
||||
|
||||
// Provision sets up all the routes by loading the modules.
|
||||
func (routes RouteList) Provision() error {
|
||||
func (routes RouteList) Provision(ctx caddy2.Context) error {
|
||||
for i, route := range routes {
|
||||
// matchers
|
||||
for modName, rawMsg := range route.Matchers {
|
||||
val, err := caddy2.LoadModule("http.matchers."+modName, rawMsg)
|
||||
val, err := ctx.LoadModule("http.matchers."+modName, rawMsg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading matcher module '%s': %v", modName, err)
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@ func (routes RouteList) Provision() error {
|
|||
|
||||
// middleware
|
||||
for j, rawMsg := range route.Apply {
|
||||
mid, err := caddy2.LoadModuleInline("middleware", "http.middleware", rawMsg)
|
||||
mid, err := ctx.LoadModuleInline("middleware", "http.middleware", rawMsg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading middleware module in position %d: %v", j, err)
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ func (routes RouteList) Provision() error {
|
|||
|
||||
// responder
|
||||
if route.Respond != nil {
|
||||
resp, err := caddy2.LoadModuleInline("responder", "http.responders", route.Respond)
|
||||
resp, err := ctx.LoadModuleInline("responder", "http.responders", route.Respond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading responder module: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,10 +40,10 @@ func (m *acmeManagerMaker) newManager(interactive bool) (certmagic.Manager, erro
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *acmeManagerMaker) Provision() error {
|
||||
func (m *acmeManagerMaker) Provision(ctx caddy2.Context) error {
|
||||
// DNS providers
|
||||
if m.Challenges.DNS != nil {
|
||||
val, err := caddy2.LoadModuleInline("provider", "tls.dns", m.Challenges.DNS)
|
||||
val, err := ctx.LoadModuleInline("provider", "tls.dns", m.Challenges.DNS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading TLS storage module: %s", err)
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ func (m *acmeManagerMaker) Provision() error {
|
|||
|
||||
// policy-specific storage implementation
|
||||
if m.Storage != nil {
|
||||
val, err := caddy2.LoadModuleInline("system", "caddy.storage", m.Storage)
|
||||
val, err := ctx.LoadModuleInline("system", "caddy.storage", m.Storage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading TLS storage module: %s", err)
|
||||
}
|
||||
|
|
@ -93,10 +93,10 @@ func (m *acmeManagerMaker) setDefaults() {
|
|||
// makeCertMagicConfig converts m into a certmagic.Config, because
|
||||
// this is a special case where the default manager is the certmagic
|
||||
// Config and not a separate manager.
|
||||
func (m *acmeManagerMaker) makeCertMagicConfig() certmagic.Config {
|
||||
func (m *acmeManagerMaker) makeCertMagicConfig(ctx caddy2.Context) certmagic.Config {
|
||||
storage := m.storage
|
||||
if storage == nil {
|
||||
storage = caddy2.GetStorage()
|
||||
storage = ctx.Storage()
|
||||
}
|
||||
|
||||
var ond *certmagic.OnDemandConfig
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ type ConnectionPolicies []*ConnectionPolicy
|
|||
// TLSConfig converts the group of policies to a standard-lib-compatible
|
||||
// TLS configuration which selects the first matching policy based on
|
||||
// the ClientHello.
|
||||
func (cp ConnectionPolicies) TLSConfig(handle caddy2.Handle) (*tls.Config, error) {
|
||||
func (cp ConnectionPolicies) TLSConfig(ctx caddy2.Context) (*tls.Config, error) {
|
||||
// connection policy matchers
|
||||
for i, pol := range cp {
|
||||
for modName, rawMsg := range pol.MatchersRaw {
|
||||
val, err := caddy2.LoadModule("tls.handshake_match."+modName, rawMsg)
|
||||
val, err := ctx.LoadModule("tls.handshake_match."+modName, rawMsg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading handshake matcher module '%s': %s", modName, err)
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ func (cp ConnectionPolicies) TLSConfig(handle caddy2.Handle) (*tls.Config, error
|
|||
|
||||
// pre-build standard TLS configs so we don't have to at handshake-time
|
||||
for i := range cp {
|
||||
err := cp[i].buildStandardTLSConfig(handle)
|
||||
err := cp[i].buildStandardTLSConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connection policy %d: building standard TLS config: %s", i, err)
|
||||
}
|
||||
|
|
@ -74,8 +74,8 @@ type ConnectionPolicy struct {
|
|||
stdTLSConfig *tls.Config
|
||||
}
|
||||
|
||||
func (cp *ConnectionPolicy) buildStandardTLSConfig(handle caddy2.Handle) error {
|
||||
tlsAppIface, err := handle.App("tls")
|
||||
func (cp *ConnectionPolicy) buildStandardTLSConfig(ctx caddy2.Context) error {
|
||||
tlsAppIface, err := ctx.App("tls")
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting tls app: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,13 @@ type TLS struct {
|
|||
|
||||
certificateLoaders []CertificateLoader
|
||||
certCache *certmagic.Cache
|
||||
ctx caddy2.Context
|
||||
}
|
||||
|
||||
// Provision sets up the configuration for the TLS app.
|
||||
func (t *TLS) Provision() error {
|
||||
func (t *TLS) Provision(ctx caddy2.Context) error {
|
||||
t.ctx = ctx
|
||||
|
||||
// set up the certificate cache
|
||||
// TODO: this makes a new cache every time; better to only make a new
|
||||
// cache (or even better, add/remove only what is necessary) if the
|
||||
|
|
@ -41,7 +44,7 @@ func (t *TLS) Provision() error {
|
|||
})
|
||||
|
||||
for i, ap := range t.Automation.Policies {
|
||||
val, err := caddy2.LoadModuleInline("module", "tls.management", ap.Management)
|
||||
val, err := ctx.LoadModuleInline("module", "tls.management", ap.Management)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading TLS automation management module: %s", err)
|
||||
}
|
||||
|
|
@ -54,7 +57,7 @@ func (t *TLS) Provision() error {
|
|||
if modName == automateKey {
|
||||
continue // special case; these will be loaded in later
|
||||
}
|
||||
val, err := caddy2.LoadModule("tls.certificates."+modName, rawMsg)
|
||||
val, err := ctx.LoadModule("tls.certificates."+modName, rawMsg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading certificate module '%s': %s", modName, err)
|
||||
}
|
||||
|
|
@ -65,7 +68,7 @@ func (t *TLS) Provision() error {
|
|||
}
|
||||
|
||||
// Start activates the TLS module.
|
||||
func (t *TLS) Start(handle caddy2.Handle) error {
|
||||
func (t *TLS) Start() error {
|
||||
// load manual/static (unmanaged) certificates
|
||||
for _, loader := range t.certificateLoaders {
|
||||
certs, err := loader.LoadCertificates()
|
||||
|
|
@ -73,7 +76,7 @@ func (t *TLS) Start(handle caddy2.Handle) error {
|
|||
return fmt.Errorf("loading certificates: %v", err)
|
||||
}
|
||||
magic := certmagic.New(t.certCache, certmagic.Config{
|
||||
Storage: caddy2.GetStorage(),
|
||||
Storage: t.ctx.Storage(),
|
||||
})
|
||||
for _, cert := range certs {
|
||||
err := magic.CacheUnmanagedTLSCertificate(cert)
|
||||
|
|
@ -114,7 +117,7 @@ func (t *TLS) Stop() error {
|
|||
func (t *TLS) Manage(names []string) error {
|
||||
for _, name := range names {
|
||||
ap := t.getAutomationPolicyForName(name)
|
||||
magic := certmagic.New(t.certCache, ap.makeCertMagicConfig())
|
||||
magic := certmagic.New(t.certCache, ap.makeCertMagicConfig(t.ctx))
|
||||
err := magic.Manage([]string{name})
|
||||
if err != nil {
|
||||
return fmt.Errorf("automate: manage %s: %v", name, err)
|
||||
|
|
@ -130,13 +133,13 @@ func (t *TLS) HandleHTTPChallenge(w http.ResponseWriter, r *http.Request) bool {
|
|||
return false
|
||||
}
|
||||
ap := t.getAutomationPolicyForName(r.Host)
|
||||
magic := certmagic.New(t.certCache, ap.makeCertMagicConfig())
|
||||
magic := certmagic.New(t.certCache, ap.makeCertMagicConfig(t.ctx))
|
||||
return magic.HandleHTTPChallenge(w, r)
|
||||
}
|
||||
|
||||
func (t *TLS) getConfigForName(name string) (certmagic.Config, error) {
|
||||
ap := t.getAutomationPolicyForName(name)
|
||||
return ap.makeCertMagicConfig(), nil
|
||||
return ap.makeCertMagicConfig(t.ctx), nil
|
||||
}
|
||||
|
||||
func (t *TLS) getAutomationPolicyForName(name string) AutomationPolicy {
|
||||
|
|
@ -178,12 +181,12 @@ type AutomationPolicy struct {
|
|||
management ManagerMaker
|
||||
}
|
||||
|
||||
func (ap AutomationPolicy) makeCertMagicConfig() certmagic.Config {
|
||||
func (ap AutomationPolicy) makeCertMagicConfig(ctx caddy2.Context) certmagic.Config {
|
||||
// default manager (ACME) is a special case because of how CertMagic is designed
|
||||
// TODO: refactor certmagic so that ACME manager is not a special case by extracting
|
||||
// its config fields out of the certmagic.Config struct, or something...
|
||||
if acmeMgmt, ok := ap.management.(*acmeManagerMaker); ok {
|
||||
return acmeMgmt.makeCertMagicConfig()
|
||||
return acmeMgmt.makeCertMagicConfig(ctx)
|
||||
}
|
||||
|
||||
return certmagic.Config{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue