From 1f0c061ce30f218e63fcc17e0fdfc8b90d754ba5 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Thu, 16 May 2019 16:05:38 -0600 Subject: Architectural shift to using context for config and module state --- modules/caddyhttp/caddyhttp.go | 55 ++++++++++++++++-------------- modules/caddyhttp/reverseproxy/module.go | 33 ------------------ modules/caddyhttp/reverseproxy/upstream.go | 4 +-- modules/caddyhttp/routes.go | 8 ++--- modules/caddytls/acmemanager.go | 10 +++--- modules/caddytls/connpolicy.go | 10 +++--- modules/caddytls/tls.go | 23 +++++++------ 7 files changed, 59 insertions(+), 84 deletions(-) (limited to 'modules') diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go index dfb2ea0..0fe9c98 100644 --- a/modules/caddyhttp/caddyhttp.go +++ b/modules/caddyhttp/caddyhttp.go @@ -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) diff --git a/modules/caddyhttp/reverseproxy/module.go b/modules/caddyhttp/reverseproxy/module.go index cc53bf5..1bfc9ad 100755 --- a/modules/caddyhttp/reverseproxy/module.go +++ b/modules/caddyhttp/reverseproxy/module.go @@ -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 - }, }) } diff --git a/modules/caddyhttp/reverseproxy/upstream.go b/modules/caddyhttp/reverseproxy/upstream.go index b521d46..7e429f9 100755 --- a/modules/caddyhttp/reverseproxy/upstream.go +++ b/modules/caddyhttp/reverseproxy/upstream.go @@ -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 { diff --git a/modules/caddyhttp/routes.go b/modules/caddyhttp/routes.go index cd612ac..d204939 100644 --- a/modules/caddyhttp/routes.go +++ b/modules/caddyhttp/routes.go @@ -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) } diff --git a/modules/caddytls/acmemanager.go b/modules/caddytls/acmemanager.go index 40e2d24..59fc7c3 100644 --- a/modules/caddytls/acmemanager.go +++ b/modules/caddytls/acmemanager.go @@ -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 diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go index bdbd79f..45fe83a 100644 --- a/modules/caddytls/connpolicy.go +++ b/modules/caddytls/connpolicy.go @@ -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) } diff --git a/modules/caddytls/tls.go b/modules/caddytls/tls.go index fbc850c..4743e6b 100644 --- a/modules/caddytls/tls.go +++ b/modules/caddytls/tls.go @@ -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{ -- cgit v1.2.3