@marcel91200 / caddy Mirror · read-only / commits / 1f0c061c
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

Architectural shift to using context for config and module state

Matthew Holt committed May 16, 2019 at 16:05 UTC 1f0c061ce30f218e63fcc17e0fdfc8b90d754ba5
11 files changed +305 -351
caddy.go
+90 -169
@@ -1,127 +1,118 @@
1 package caddy2
2
3 import (
4 + "context"
5 "encoding/json"
6 "fmt"
7 "log"
8 "strings"
9 "sync"
9 - "sync/atomic"
10 "time"
11
12 "github.com/mholt/certmagic"
13 )
14
15 // Run runs Caddy with the given config.
16 -func Run(cfg *Config) error {
17 - // allow only one call to Start at a time,
18 - // since various calls to LoadModule()
19 - // access shared map moduleInstances
20 - startMu.Lock()
21 - defer startMu.Unlock()
22 -
23 - // because we will need to roll back any state
24 - // modifications if this function errors, we
25 - // keep a single error value and scope all
26 - // sub-operations to their own functions to
27 - // ensure this error value does not get
28 - // overridden or missed when it should have
29 - // been set by a short assignment
30 - var err error
31 -
32 - // prepare the new config for use
33 - cfg.apps = make(map[string]App)
34 - cfg.moduleStates = make(map[string]interface{})
35 -
36 - // reset the shared moduleInstances map; but
37 - // keep a temporary reference to the current
38 - // one so we can transfer over any necessary
39 - // state to the new modules or to roll back
40 - // if necessary
41 - oldModuleInstances := moduleInstances
42 - defer func() {
43 - if err != nil {
44 - moduleInstances = oldModuleInstances
45 - }
46 - }()
47 - moduleInstances = make(map[string][]interface{})
48 -
49 - // set up storage and make it CertMagic's default storage, too
50 - err = func() error {
51 - if cfg.StorageRaw != nil {
52 - val, err := LoadModuleInline("system", "caddy.storage", cfg.StorageRaw)
53 - if err != nil {
54 - return fmt.Errorf("loading storage module: %v", err)
55 - }
56 - stor, err := val.(StorageConverter).CertMagicStorage()
16 +func Run(newCfg *Config) error {
17 + currentCfgMu.Lock()
18 + defer currentCfgMu.Unlock()
19 +
20 + if newCfg != nil {
21 + // because we will need to roll back any state
22 + // modifications if this function errors, we
23 + // keep a single error value and scope all
24 + // sub-operations to their own functions to
25 + // ensure this error value does not get
26 + // overridden or missed when it should have
27 + // been set by a short assignment
28 + var err error
29 +
30 + // prepare the new config for use
31 + newCfg.apps = make(map[string]App)
32 +
33 + // create a context within which to load
34 + // modules - essentially our new config's
35 + // execution environment; be sure that
36 + // cleanup occurs when we return if there
37 + // was an error; otherwise, it will get
38 + // cleaned up on next config cycle
39 + ctx, cancel := NewContext(Context{Context: context.Background(), cfg: newCfg})
40 + defer func() {
41 if err != nil {
58 - return fmt.Errorf("creating storage value: %v", err)
42 + cancel() // clean up now
43 }
60 - cfg.storage = stor
61 - cfg.StorageRaw = nil // allow GC to deallocate - TODO: Does this help?
62 - }
63 - if cfg.storage == nil {
64 - cfg.storage = &certmagic.FileStorage{Path: dataDir()}
65 - }
66 - certmagic.Default.Storage = cfg.storage
44 + }()
45 + newCfg.cancelFunc = cancel // clean up later
46
68 - return nil
69 - }()
70 - if err != nil {
71 - return err
72 - }
73 -
74 - // Load, Provision, Validate
75 - err = func() error {
76 - for modName, rawMsg := range cfg.AppsRaw {
77 - val, err := LoadModule(modName, rawMsg)
78 - if err != nil {
79 - return fmt.Errorf("loading app module '%s': %v", modName, err)
47 + // set up storage and make it CertMagic's default storage, too
48 + err = func() error {
49 + if newCfg.StorageRaw != nil {
50 + val, err := ctx.LoadModuleInline("system", "caddy.storage", newCfg.StorageRaw)
51 + if err != nil {
52 + return fmt.Errorf("loading storage module: %v", err)
53 + }
54 + stor, err := val.(StorageConverter).CertMagicStorage()
55 + if err != nil {
56 + return fmt.Errorf("creating storage value: %v", err)
57 + }
58 + newCfg.storage = stor
59 + newCfg.StorageRaw = nil // allow GC to deallocate - TODO: Does this help?
60 + }
61 + if newCfg.storage == nil {
62 + newCfg.storage = &certmagic.FileStorage{Path: dataDir()}
63 }
81 - cfg.apps[modName] = val.(App)
64 + certmagic.Default.Storage = newCfg.storage
65 +
66 + return nil
67 + }()
68 + if err != nil {
69 + return err
70 }
83 - return nil
84 - }()
85 - if err != nil {
86 - return err
87 - }
71
89 - // swap old config with the new one, and
90 - // roll back this change if anything fails
91 - currentCfgMu.Lock()
92 - oldCfg := currentCfg
93 - currentCfg = cfg
94 - currentCfgMu.Unlock()
95 - defer func() {
72 + // Load, Provision, Validate
73 + err = func() error {
74 + for modName, rawMsg := range newCfg.AppsRaw {
75 + val, err := ctx.LoadModule(modName, rawMsg)
76 + if err != nil {
77 + return fmt.Errorf("loading app module '%s': %v", modName, err)
78 + }
79 + newCfg.apps[modName] = val.(App)
80 + }
81 + return nil
82 + }()
83 if err != nil {
97 - currentCfgMu.Lock()
98 - currentCfg = oldCfg
99 - currentCfgMu.Unlock()
84 + return err
85 }
101 - }()
86
103 - // Start
104 - err = func() error {
105 - h := Handle{cfg}
106 - for name, a := range cfg.apps {
107 - err := a.Start(h)
108 - if err != nil {
109 - for otherAppName, otherApp := range cfg.apps {
110 - err := otherApp.Stop()
111 - if err != nil {
112 - log.Printf("aborting app %s: %v", otherAppName, err)
87 + // Start
88 + err = func() error {
89 + var started []string
90 + for name, a := range newCfg.apps {
91 + err := a.Start()
92 + if err != nil {
93 + for _, otherAppName := range started {
94 + err2 := newCfg.apps[otherAppName].Stop()
95 + if err2 != nil {
96 + err = fmt.Errorf("%v; additionally, aborting app %s: %v",
97 + err, otherAppName, err2)
98 + }
99 }
100 + return fmt.Errorf("%s app module: start: %v", name, err)
101 }
115 - return fmt.Errorf("%s app module: start: %v", name, err)
102 + started = append(started, name)
103 }
104 + return nil
105 + }()
106 + if err != nil {
107 + return err
108 }
118 - return nil
119 - }()
120 - if err != nil {
121 - return err
109 }
110
124 - // Stop
111 + // swap old config with the new one
112 + oldCfg := currentCfg
113 + currentCfg = newCfg
114 +
115 + // Stop, Cleanup
116 if oldCfg != nil {
117 for name, a := range oldCfg.apps {
118 err := a.Stop()
@@ -129,26 +120,9 @@ func Run(cfg *Config) error {
120 log.Printf("[ERROR] stop %s: %v", name, err)
121 }
122 }
132 - }
123
134 - // shut down listeners that are no longer being used
135 - err = func() error {
136 - listenersMu.Lock()
137 - for key, info := range listeners {
138 - if atomic.LoadInt32(&info.usage) == 0 {
139 - err := info.ln.Close()
140 - if err != nil {
141 - log.Printf("[ERROR] closing listener %s: %v", info.ln.Addr(), err)
142 - continue
143 - }
144 - delete(listeners, key)
145 - }
146 - }
147 - listenersMu.Unlock()
148 - return nil
149 - }()
150 - if err != nil {
151 - return err
124 + // clean up old modules
125 + oldCfg.cancelFunc()
126 }
127
128 return nil
@@ -156,7 +130,7 @@ func Run(cfg *Config) error {
130
131 // App is a thing that Caddy runs.
132 type App interface {
159 - Start(Handle) error
133 + Start() error
134 Stop() error
135 }
136
@@ -172,46 +146,7 @@ type Config struct {
146 // keyed by module name.
147 apps map[string]App
148
175 - // moduleStates stores the optional "global" state
176 - // values of every module used by this configuration,
177 - // keyed by module name.
178 - moduleStates map[string]interface{}
179 -}
180 -
181 -// Handle allows app modules to access
182 -// the top-level Config in a controlled
183 -// manner without needing to rely on
184 -// global state.
185 -type Handle struct {
186 - current *Config
187 -}
188 -
189 -// App returns the configured app named name. If no app with
190 -// that name is currently configured, a new empty one will be
191 -// instantiated. (The app module must still be registered.)
192 -func (h Handle) App(name string) (interface{}, error) {
193 - if app, ok := h.current.apps[name]; ok {
194 - return app, nil
195 - }
196 - modVal, err := LoadModule(name, nil)
197 - if err != nil {
198 - return nil, fmt.Errorf("instantiating new module %s: %v", name, err)
199 - }
200 - h.current.apps[name] = modVal.(App)
201 - return modVal, nil
202 -}
203 -
204 -// GetStorage returns the configured Caddy storage implementation.
205 -// If no storage implementation is explicitly configured, the
206 -// default one is returned instead, as long as there is a current
207 -// configuration loaded.
208 -func GetStorage() certmagic.Storage {
209 - currentCfgMu.RLock()
210 - defer currentCfgMu.RUnlock()
211 - if currentCfg == nil {
212 - return nil
213 - }
214 - return currentCfg.storage
149 + cancelFunc context.CancelFunc
150 }
151
152 // Duration is a JSON-string-unmarshable duration type.
@@ -236,17 +171,3 @@ var (
171 currentCfg *Config
172 currentCfgMu sync.RWMutex
173 )
239 -
240 -// moduleInstances stores the individual instantiated
241 -// values of modules, keyed by module name. The list
242 -// of instances of each module get passed into the
243 -// respective module's OnLoad callback, so they can
244 -// set up any global state and/or make sure their
245 -// configuration, when viewed as a whole, is valid.
246 -// Since this list is shared, only one Start() routine
247 -// must be allowed to happen at any given time.
248 -var moduleInstances = make(map[string][]interface{})
249 -
250 -// startMu ensures that only one Start() happens at a time.
251 -// This is important since moduleInstances is shared state.
252 -var startMu sync.Mutex
context.go new
+126
@@ -0,0 +1,126 @@
1 +package caddy2
2 +
3 +import (
4 + "context"
5 + "encoding/json"
6 + "fmt"
7 + "log"
8 + "reflect"
9 +
10 + "github.com/mholt/certmagic"
11 +)
12 +
13 +type Context struct {
14 + context.Context
15 + moduleInstances map[string][]interface{}
16 + cfg *Config
17 +}
18 +
19 +func NewContext(ctx Context) (Context, context.CancelFunc) {
20 + newCtx := Context{moduleInstances: make(map[string][]interface{}), cfg: ctx.cfg}
21 + c, cancel := context.WithCancel(ctx.Context)
22 + wrappedCancel := func() {
23 + cancel()
24 + for modName, modInstances := range newCtx.moduleInstances {
25 + for _, inst := range modInstances {
26 + if cu, ok := inst.(CleanerUpper); ok {
27 + err := cu.Cleanup()
28 + if err != nil {
29 + log.Printf("[ERROR] %s (%p): cleanup: %v", modName, inst, err)
30 + }
31 + }
32 + }
33 + }
34 + }
35 + newCtx.Context = c
36 + return newCtx, wrappedCancel
37 +}
38 +
39 +func (ctx Context) LoadModule(name string, rawMsg json.RawMessage) (interface{}, error) {
40 + modulesMu.Lock()
41 + mod, ok := modules[name]
42 + modulesMu.Unlock()
43 + if !ok {
44 + return nil, fmt.Errorf("unknown module: %s", name)
45 + }
46 +
47 + if mod.New == nil {
48 + return nil, fmt.Errorf("module '%s' has no constructor", mod.Name)
49 + }
50 +
51 + val, err := mod.New()
52 + if err != nil {
53 + return nil, fmt.Errorf("initializing module '%s': %v", mod.Name, err)
54 + }
55 +
56 + // value must be a pointer for unmarshaling into concrete type
57 + if rv := reflect.ValueOf(val); rv.Kind() != reflect.Ptr {
58 + val = reflect.New(rv.Type()).Elem().Addr().Interface()
59 + }
60 +
61 + // fill in its config only if there is a config to fill in
62 + if len(rawMsg) > 0 {
63 + err = json.Unmarshal(rawMsg, &val)
64 + if err != nil {
65 + return nil, fmt.Errorf("decoding module config: %s: %v", mod.Name, err)
66 + }
67 + }
68 +
69 + if prov, ok := val.(Provisioner); ok {
70 + err := prov.Provision(ctx)
71 + if err != nil {
72 + return nil, fmt.Errorf("provision %s: %v", mod.Name, err)
73 + }
74 + }
75 +
76 + if validator, ok := val.(Validator); ok {
77 + err := validator.Validate(ctx)
78 + if err != nil {
79 + if cleanerUpper, ok := val.(CleanerUpper); ok {
80 + err2 := cleanerUpper.Cleanup()
81 + if err2 != nil {
82 + err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2)
83 + }
84 + return nil, fmt.Errorf("%s: invalid configuration: %v", mod.Name, err)
85 + }
86 + }
87 + }
88 +
89 + ctx.moduleInstances[name] = append(ctx.moduleInstances[name], val)
90 +
91 + return val, nil
92 +}
93 +
94 +func (ctx Context) LoadModuleInline(moduleNameKey, moduleScope string, raw json.RawMessage) (interface{}, error) {
95 + moduleName, err := getModuleNameInline(moduleNameKey, raw)
96 + if err != nil {
97 + return nil, err
98 + }
99 +
100 + val, err := ctx.LoadModule(moduleScope+"."+moduleName, raw)
101 + if err != nil {
102 + return nil, fmt.Errorf("loading module '%s': %v", moduleName, err)
103 + }
104 +
105 + return val, nil
106 +}
107 +
108 +// App returns the configured app named name. If no app with
109 +// that name is currently configured, a new empty one will be
110 +// instantiated. (The app module must still be registered.)
111 +func (ctx Context) App(name string) (interface{}, error) {
112 + if app, ok := ctx.cfg.apps[name]; ok {
113 + return app, nil
114 + }
115 + modVal, err := ctx.LoadModule(name, nil)
116 + if err != nil {
117 + return nil, fmt.Errorf("instantiating new module %s: %v", name, err)
118 + }
119 + ctx.cfg.apps[name] = modVal.(App)
120 + return modVal, nil
121 +}
122 +
123 +// Storage returns the configured Caddy storage implementation.
124 +func (ctx Context) Storage() certmagic.Storage {
125 + return ctx.cfg.storage
126 +}
listeners.go
+23 -10
@@ -9,6 +9,7 @@ import (
9 )
10
11 // Listen returns a listener suitable for use in a Caddy module.
12 +// Always be sure to close listeners when you are done with them.
13 func Listen(network, addr string) (net.Listener, error) {
14 lnKey := network + "/" + addr
15
@@ -16,9 +17,9 @@ func Listen(network, addr string) (net.Listener, error) {
17 defer listenersMu.Unlock()
18
19 // if listener already exists, increment usage counter, then return listener
19 - if lnInfo, ok := listeners[lnKey]; ok {
20 - atomic.AddInt32(&lnInfo.usage, 1)
21 - return &fakeCloseListener{usage: &lnInfo.usage, Listener: lnInfo.ln}, nil
20 + if lnUsage, ok := listeners[lnKey]; ok {
21 + atomic.AddInt32(&lnUsage.usage, 1)
22 + return &fakeCloseListener{usage: &lnUsage.usage, key: lnKey, Listener: lnUsage.ln}, nil
23 }
24
25 // or, create new one and save it
@@ -28,10 +29,10 @@ func Listen(network, addr string) (net.Listener, error) {
29 }
30
31 // make sure to start its usage counter at 1
31 - lnInfo := &listenerUsage{usage: 1, ln: ln}
32 - listeners[lnKey] = lnInfo
32 + lnUsage := &listenerUsage{usage: 1, ln: ln}
33 + listeners[lnKey] = lnUsage
34
34 - return &fakeCloseListener{usage: &lnInfo.usage, Listener: ln}, nil
35 + return &fakeCloseListener{usage: &lnUsage.usage, key: lnKey, Listener: ln}, nil
36 }
37
38 // fakeCloseListener's Close() method is a no-op. This allows
@@ -42,6 +43,7 @@ func Listen(network, addr string) (net.Listener, error) {
43 type fakeCloseListener struct {
44 closed int32 // accessed atomically
45 usage *int32 // accessed atomically
46 + key string
47 net.Listener
48 }
49
@@ -80,8 +82,9 @@ func (fcl *fakeCloseListener) Accept() (net.Conn, error) {
82 return nil, err
83 }
84
83 -// Close stops accepting new connections, but does not
84 -// actually close the underlying listener.
85 +// Close stops accepting new connections without
86 +// closing the underlying listener, unless no one
87 +// else is using it.
88 func (fcl *fakeCloseListener) Close() error {
89 if atomic.CompareAndSwapInt32(&fcl.closed, 0, 1) {
90 // unfortunately, there is no way to cancel any
@@ -99,8 +102,18 @@ func (fcl *fakeCloseListener) Close() error {
102 }
103
104 // since we're no longer using this listener,
102 - // decrement the usage counter
103 - atomic.AddInt32(fcl.usage, -1)
105 + // decrement the usage counter and, if no one
106 + // else is using it, close underlying listener
107 + if atomic.AddInt32(fcl.usage, -1) == 0 {
108 + listenersMu.Lock()
109 + delete(listeners, fcl.key)
110 + listenersMu.Unlock()
111 + err := fcl.Listener.Close()
112 + if err != nil {
113 + return err
114 + }
115 + }
116 +
117 }
118
119 return nil
modules.go
+7 -88
@@ -3,7 +3,6 @@ package caddy2
3 import (
4 "encoding/json"
5 "fmt"
6 - "reflect"
6 "sort"
7 "strings"
8 "sync"
@@ -32,9 +31,6 @@ func RegisterModule(mod Module) error {
31 if mod.Name == "caddy" {
32 return fmt.Errorf("modules cannot be named 'caddy'")
33 }
35 - if strings.HasPrefix(mod.Name, "caddy.") {
36 - return fmt.Errorf("modules cannot be namespaced in 'caddy'")
37 - }
34
35 modulesMu.Lock()
36 defer modulesMu.Unlock()
@@ -123,88 +119,6 @@ func Modules() []string {
119 return names
120 }
121
126 -// LoadModule decodes rawMsg into a new instance of mod and
127 -// returns the value. If mod.New() does not return a pointer
128 -// value, it is converted to one so that it is unmarshaled
129 -// into the underlying concrete type. If mod.New is nil, an
130 -// error is returned. If the module implements Validator or
131 -// Provisioner interfaces, those methods are invoked to
132 -// ensure the module is fully configured and valid before
133 -// being used.
134 -func LoadModule(name string, rawMsg json.RawMessage) (interface{}, error) {
135 - modulesMu.Lock()
136 - mod, ok := modules[name]
137 - modulesMu.Unlock()
138 - if !ok {
139 - return nil, fmt.Errorf("unknown module: %s", name)
140 - }
141 -
142 - if mod.New == nil {
143 - return nil, fmt.Errorf("module '%s' has no constructor", mod.Name)
144 - }
145 -
146 - val, err := mod.New()
147 - if err != nil {
148 - return nil, fmt.Errorf("initializing module '%s': %v", mod.Name, err)
149 - }
150 -
151 - // value must be a pointer for unmarshaling into concrete type
152 - if rv := reflect.ValueOf(val); rv.Kind() != reflect.Ptr {
153 - val = reflect.New(rv.Type()).Elem().Addr().Interface()
154 - }
155 -
156 - // fill in its config only if there is a config to fill in
157 - if len(rawMsg) > 0 {
158 - err = json.Unmarshal(rawMsg, &val)
159 - if err != nil {
160 - return nil, fmt.Errorf("decoding module config: %s: %v", mod.Name, err)
161 - }
162 - }
163 -
164 - if prov, ok := val.(Provisioner); ok {
165 - err := prov.Provision()
166 - if err != nil {
167 - return nil, fmt.Errorf("provision %s: %v", mod.Name, err)
168 - }
169 - }
170 -
171 - if validator, ok := val.(Validator); ok {
172 - err := validator.Validate()
173 - if err != nil {
174 - return nil, fmt.Errorf("%s: invalid configuration: %v", mod.Name, err)
175 - }
176 - }
177 -
178 - moduleInstances[mod.Name] = append(moduleInstances[mod.Name], val)
179 -
180 - return val, nil
181 -}
182 -
183 -// LoadModuleInline loads a module from a JSON raw message which decodes
184 -// to a map[string]interface{}, where one of the keys is moduleNameKey
185 -// and the corresponding value is the module name as a string, which
186 -// can be found in the given scope.
187 -//
188 -// This allows modules to be decoded into their concrete types and
189 -// used when their names cannot be the unique key in a map, such as
190 -// when there are multiple instances in the map or it appears in an
191 -// array (where there are no custom keys). In other words, the key
192 -// containing the module name is treated special/separate from all
193 -// the other keys.
194 -func LoadModuleInline(moduleNameKey, moduleScope string, raw json.RawMessage) (interface{}, error) {
195 - moduleName, err := getModuleNameInline(moduleNameKey, raw)
196 - if err != nil {
197 - return nil, err
198 - }
199 -
200 - val, err := LoadModule(moduleScope+"."+moduleName, raw)
201 - if err != nil {
202 - return nil, fmt.Errorf("loading module '%s': %v", moduleName, err)
203 - }
204 -
205 - return val, nil
206 -}
207 -
122 // getModuleNameInline loads the string value from raw of moduleNameKey,
123 // where raw must be a JSON encoding of a map.
124 func getModuleNameInline(moduleNameKey string, raw json.RawMessage) (string, error) {
@@ -228,14 +142,19 @@ func getModuleNameInline(moduleNameKey string, raw json.RawMessage) (string, err
142 // always be fast (imperceptible running time) and an error should
143 // be returned only if the value's configuration is invalid.
144 type Validator interface {
231 - Validate() error
145 + Validate(Context) error
146 }
147
148 // Provisioner is implemented by modules which may need to perform
149 // some additional "setup" steps immediately after being loaded.
150 // This method will be called after Validate() (if implemented).
151 type Provisioner interface {
238 - Provision() error
152 + Provision(Context) error
153 +}
154 +
155 +// TODO: different name...
156 +type CleanerUpper interface {
157 + Cleanup() error
158 }
159
160 var (
modules/caddyhttp/caddyhttp.go
+30 -25
@@ -37,16 +37,20 @@ type App struct {
37 Servers map[string]*Server `json:"servers"`
38
39 servers []*http.Server
40 +
41 + ctx caddy2.Context
42 }
43
44 // Provision sets up the app.
43 -func (hc *App) Provision() error {
44 - for _, srv := range hc.Servers {
45 - err := srv.Routes.Provision()
45 +func (app *App) Provision(ctx caddy2.Context) error {
46 + app.ctx = ctx
47 +
48 + for _, srv := range app.Servers {
49 + err := srv.Routes.Provision(ctx)
50 if err != nil {
51 return fmt.Errorf("setting up server routes: %v", err)
52 }
49 - err = srv.Errors.Routes.Provision()
53 + err = srv.Errors.Routes.Provision(ctx)
54 if err != nil {
55 return fmt.Errorf("setting up server error handling routes: %v", err)
56 }
@@ -56,10 +60,10 @@ func (hc *App) Provision() error {
60 }
61
62 // Validate ensures the app's configuration is valid.
59 -func (hc *App) Validate() error {
63 +func (app *App) Validate() error {
64 // each server must use distinct listener addresses
65 lnAddrs := make(map[string]string)
62 - for srvName, srv := range hc.Servers {
66 + for srvName, srv := range app.Servers {
67 for _, addr := range srv.Listen {
68 netw, expanded, err := parseListenAddr(addr)
69 if err != nil {
@@ -78,13 +82,13 @@ func (hc *App) Validate() error {
82 }
83
84 // Start runs the app. It sets up automatic HTTPS if enabled.
81 -func (hc *App) Start(handle caddy2.Handle) error {
82 - err := hc.automaticHTTPS(handle)
85 +func (app *App) Start() error {
86 + err := app.automaticHTTPS()
87 if err != nil {
88 return fmt.Errorf("enabling automatic HTTPS: %v", err)
89 }
90
87 - for srvName, srv := range hc.Servers {
91 + for srvName, srv := range app.Servers {
92 s := &http.Server{
93 ReadTimeout: time.Duration(srv.ReadTimeout),
94 ReadHeaderTimeout: time.Duration(srv.ReadHeaderTimeout),
@@ -110,13 +114,13 @@ func (hc *App) Start(handle caddy2.Handle) error {
114 }
115
116 // enable TLS
113 - httpPort := hc.HTTPPort
117 + httpPort := app.HTTPPort
118 if httpPort == 0 {
119 httpPort = DefaultHTTPPort
120 }
121 _, port, _ := net.SplitHostPort(addr)
122 if len(srv.TLSConnPolicies) > 0 && port != strconv.Itoa(httpPort) {
119 - tlsCfg, err := srv.TLSConnPolicies.TLSConfig(handle)
123 + tlsCfg, err := srv.TLSConnPolicies.TLSConfig(app.ctx)
124 if err != nil {
125 return fmt.Errorf("%s/%s: making TLS configuration: %v", network, addr, err)
126 }
@@ -124,7 +128,7 @@ func (hc *App) Start(handle caddy2.Handle) error {
128 }
129
130 go s.Serve(ln)
127 - hc.servers = append(hc.servers, s)
131 + app.servers = append(app.servers, s)
132 }
133 }
134 }
@@ -133,14 +137,14 @@ func (hc *App) Start(handle caddy2.Handle) error {
137 }
138
139 // Stop gracefully shuts down the HTTP server.
136 -func (hc *App) Stop() error {
140 +func (app *App) Stop() error {
141 ctx := context.Background()
138 - if hc.GracePeriod > 0 {
142 + if app.GracePeriod > 0 {
143 var cancel context.CancelFunc
140 - ctx, cancel = context.WithTimeout(ctx, time.Duration(hc.GracePeriod))
144 + ctx, cancel = context.WithTimeout(ctx, time.Duration(app.GracePeriod))
145 defer cancel()
146 }
143 - for _, s := range hc.servers {
147 + for _, s := range app.servers {
148 err := s.Shutdown(ctx)
149 if err != nil {
150 return err
@@ -149,8 +153,8 @@ func (hc *App) Stop() error {
153 return nil
154 }
155
152 -func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
153 - tlsAppIface, err := handle.App("tls")
156 +func (app *App) automaticHTTPS() error {
157 + tlsAppIface, err := app.ctx.App("tls")
158 if err != nil {
159 return fmt.Errorf("getting tls app: %v", err)
160 }
@@ -159,7 +163,7 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
163 lnAddrMap := make(map[string]struct{})
164 var redirRoutes RouteList
165
162 - for srvName, srv := range hc.Servers {
166 + for srvName, srv := range app.Servers {
167 srv.tlsApp = tlsApp
168
169 if srv.DisableAutoHTTPS {
@@ -209,7 +213,7 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
213 if err != nil {
214 return fmt.Errorf("%s: invalid listener address: %v", srvName, addr)
215 }
212 - httpRedirLnAddr := joinListenAddr(netw, host, strconv.Itoa(hc.HTTPPort))
216 + httpRedirLnAddr := joinListenAddr(netw, host, strconv.Itoa(app.HTTPPort))
217 lnAddrMap[httpRedirLnAddr] = struct{}{}
218
219 if parts := strings.SplitN(port, "-", 2); len(parts) == 2 {
@@ -217,7 +221,7 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
221 }
222 redirTo := "https://{request.host}"
223
220 - httpsPort := hc.HTTPSPort
224 + httpsPort := app.HTTPSPort
225 if httpsPort == 0 {
226 httpsPort = DefaultHTTPSPort
227 }
@@ -253,13 +257,13 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
257 continue
258 }
259 for _, a := range addrs {
256 - if hc.listenerTaken(netw, a) {
260 + if app.listenerTaken(netw, a) {
261 continue mapLoop
262 }
263 }
264 lnAddrs = append(lnAddrs, addr)
265 }
262 - hc.Servers["auto_https_redirects"] = &Server{
266 + app.Servers["auto_https_redirects"] = &Server{
267 Listen: lnAddrs,
268 Routes: redirRoutes,
269 DisableAutoHTTPS: true,
@@ -269,8 +273,8 @@ func (hc *App) automaticHTTPS(handle caddy2.Handle) error {
273 return nil
274 }
275
272 -func (hc *App) listenerTaken(network, address string) bool {
273 - for _, srv := range hc.Servers {
276 +func (app *App) listenerTaken(network, address string) bool {
277 + for _, srv := range app.Servers {
278 for _, addr := range srv.Listen {
279 netw, addrs, err := parseListenAddr(addr)
280 if err != nil || netw != network {
@@ -491,3 +495,4 @@ const (
495
496 // Interface guards
497 var _ HTTPInterfaces = middlewareResponseWriter{}
498 +var _ caddy2.App = (*App)(nil)
modules/caddyhttp/reverseproxy/module.go
-33
@@ -1,8 +1,6 @@
1 package reverseproxy
2
3 import (
4 - "fmt"
5 -
4 "bitbucket.org/lightcodelabs/caddy2"
5 )
6
@@ -11,36 +9,5 @@ func init() {
9 caddy2.RegisterModule(caddy2.Module{
10 Name: "http.responders.reverse_proxy",
11 New: func() (interface{}, error) { return new(LoadBalanced), nil },
14 - OnLoad: func(instances []interface{}, _ interface{}) (interface{}, error) {
15 - // we don't need to do anything with prior state because healthcheckers are
16 - // cleaned up in OnUnload.
17 - s := &State{
18 - HealthCheckers: []*HealthChecker{},
19 - }
20 -
21 - for _, i := range instances {
22 - lb := i.(*LoadBalanced)
23 -
24 - err := NewLoadBalancedReverseProxy(lb, s)
25 - if err != nil {
26 - return nil, err
27 - }
28 - }
29 -
30 - return s, nil
31 - },
32 - OnUnload: func(state interface{}) error {
33 - s, ok := state.(*State)
34 - if !ok {
35 - return fmt.Errorf("proxy OnLoad: prior state not expected proxy.State type")
36 - }
37 -
38 - // cleanup old healthcheckers
39 - for _, hc := range s.HealthCheckers {
40 - hc.Stop()
41 - }
42 -
43 - return nil
44 - },
12 })
13 }
modules/caddyhttp/reverseproxy/upstream.go
+2 -2
@@ -76,7 +76,7 @@ var (
76 )
77
78 // NewLoadBalancedReverseProxy returns a collection of Upstreams that are to be loadbalanced.
79 -func NewLoadBalancedReverseProxy(lb *LoadBalanced, state *State) error {
79 +func NewLoadBalancedReverseProxy(lb *LoadBalanced, state *State, ctx caddy2.Context) error {
80 // set defaults
81 if lb.NoHealthyUpstreamsMessage == "" {
82 lb.NoHealthyUpstreamsMessage = msgNoHealthyUpstreams
@@ -115,7 +115,7 @@ func NewLoadBalancedReverseProxy(lb *LoadBalanced, state *State) error {
115
116 if uc.CircuitBreaker != nil {
117 if _, err := caddy2.GetModule(cbModule); err == nil {
118 - val, err := caddy2.LoadModule(cbModule, uc.CircuitBreaker)
118 + val, err := ctx.LoadModule(cbModule, uc.CircuitBreaker)
119 if err == nil {
120 cbv, ok := val.(CircuitBreaker)
121 if ok {
modules/caddyhttp/routes.go
+4 -4
@@ -29,11 +29,11 @@ type ServerRoute struct {
29 type RouteList []ServerRoute
30
31 // Provision sets up all the routes by loading the modules.
32 -func (routes RouteList) Provision() error {
32 +func (routes RouteList) Provision(ctx caddy2.Context) error {
33 for i, route := range routes {
34 // matchers
35 for modName, rawMsg := range route.Matchers {
36 - val, err := caddy2.LoadModule("http.matchers."+modName, rawMsg)
36 + val, err := ctx.LoadModule("http.matchers."+modName, rawMsg)
37 if err != nil {
38 return fmt.Errorf("loading matcher module '%s': %v", modName, err)
39 }
@@ -43,7 +43,7 @@ func (routes RouteList) Provision() error {
43
44 // middleware
45 for j, rawMsg := range route.Apply {
46 - mid, err := caddy2.LoadModuleInline("middleware", "http.middleware", rawMsg)
46 + mid, err := ctx.LoadModuleInline("middleware", "http.middleware", rawMsg)
47 if err != nil {
48 return fmt.Errorf("loading middleware module in position %d: %v", j, err)
49 }
@@ -53,7 +53,7 @@ func (routes RouteList) Provision() error {
53
54 // responder
55 if route.Respond != nil {
56 - resp, err := caddy2.LoadModuleInline("responder", "http.responders", route.Respond)
56 + resp, err := ctx.LoadModuleInline("responder", "http.responders", route.Respond)
57 if err != nil {
58 return fmt.Errorf("loading responder module: %v", err)
59 }
modules/caddytls/acmemanager.go
+5 -5
@@ -40,10 +40,10 @@ func (m *acmeManagerMaker) newManager(interactive bool) (certmagic.Manager, erro
40 return nil, nil
41 }
42
43 -func (m *acmeManagerMaker) Provision() error {
43 +func (m *acmeManagerMaker) Provision(ctx caddy2.Context) error {
44 // DNS providers
45 if m.Challenges.DNS != nil {
46 - val, err := caddy2.LoadModuleInline("provider", "tls.dns", m.Challenges.DNS)
46 + val, err := ctx.LoadModuleInline("provider", "tls.dns", m.Challenges.DNS)
47 if err != nil {
48 return fmt.Errorf("loading TLS storage module: %s", err)
49 }
@@ -53,7 +53,7 @@ func (m *acmeManagerMaker) Provision() error {
53
54 // policy-specific storage implementation
55 if m.Storage != nil {
56 - val, err := caddy2.LoadModuleInline("system", "caddy.storage", m.Storage)
56 + val, err := ctx.LoadModuleInline("system", "caddy.storage", m.Storage)
57 if err != nil {
58 return fmt.Errorf("loading TLS storage module: %s", err)
59 }
@@ -93,10 +93,10 @@ func (m *acmeManagerMaker) setDefaults() {
93 // makeCertMagicConfig converts m into a certmagic.Config, because
94 // this is a special case where the default manager is the certmagic
95 // Config and not a separate manager.
96 -func (m *acmeManagerMaker) makeCertMagicConfig() certmagic.Config {
96 +func (m *acmeManagerMaker) makeCertMagicConfig(ctx caddy2.Context) certmagic.Config {
97 storage := m.storage
98 if storage == nil {
99 - storage = caddy2.GetStorage()
99 + storage = ctx.Storage()
100 }
101
102 var ond *certmagic.OnDemandConfig
modules/caddytls/connpolicy.go
+5 -5
@@ -18,11 +18,11 @@ type ConnectionPolicies []*ConnectionPolicy
18 // TLSConfig converts the group of policies to a standard-lib-compatible
19 // TLS configuration which selects the first matching policy based on
20 // the ClientHello.
21 -func (cp ConnectionPolicies) TLSConfig(handle caddy2.Handle) (*tls.Config, error) {
21 +func (cp ConnectionPolicies) TLSConfig(ctx caddy2.Context) (*tls.Config, error) {
22 // connection policy matchers
23 for i, pol := range cp {
24 for modName, rawMsg := range pol.MatchersRaw {
25 - val, err := caddy2.LoadModule("tls.handshake_match."+modName, rawMsg)
25 + val, err := ctx.LoadModule("tls.handshake_match."+modName, rawMsg)
26 if err != nil {
27 return nil, fmt.Errorf("loading handshake matcher module '%s': %s", modName, err)
28 }
@@ -33,7 +33,7 @@ func (cp ConnectionPolicies) TLSConfig(handle caddy2.Handle) (*tls.Config, error
33
34 // pre-build standard TLS configs so we don't have to at handshake-time
35 for i := range cp {
36 - err := cp[i].buildStandardTLSConfig(handle)
36 + err := cp[i].buildStandardTLSConfig(ctx)
37 if err != nil {
38 return nil, fmt.Errorf("connection policy %d: building standard TLS config: %s", i, err)
39 }
@@ -74,8 +74,8 @@ type ConnectionPolicy struct {
74 stdTLSConfig *tls.Config
75 }
76
77 -func (cp *ConnectionPolicy) buildStandardTLSConfig(handle caddy2.Handle) error {
78 - tlsAppIface, err := handle.App("tls")
77 +func (cp *ConnectionPolicy) buildStandardTLSConfig(ctx caddy2.Context) error {
78 + tlsAppIface, err := ctx.App("tls")
79 if err != nil {
80 return fmt.Errorf("getting tls app: %v", err)
81 }
modules/caddytls/tls.go
+13 -10
@@ -26,10 +26,13 @@ type TLS struct {
26
27 certificateLoaders []CertificateLoader
28 certCache *certmagic.Cache
29 + ctx caddy2.Context
30 }
31
32 // Provision sets up the configuration for the TLS app.
32 -func (t *TLS) Provision() error {
33 +func (t *TLS) Provision(ctx caddy2.Context) error {
34 + t.ctx = ctx
35 +
36 // set up the certificate cache
37 // TODO: this makes a new cache every time; better to only make a new
38 // cache (or even better, add/remove only what is necessary) if the
@@ -41,7 +44,7 @@ func (t *TLS) Provision() error {
44 })
45
46 for i, ap := range t.Automation.Policies {
44 - val, err := caddy2.LoadModuleInline("module", "tls.management", ap.Management)
47 + val, err := ctx.LoadModuleInline("module", "tls.management", ap.Management)
48 if err != nil {
49 return fmt.Errorf("loading TLS automation management module: %s", err)
50 }
@@ -54,7 +57,7 @@ func (t *TLS) Provision() error {
57 if modName == automateKey {
58 continue // special case; these will be loaded in later
59 }
57 - val, err := caddy2.LoadModule("tls.certificates."+modName, rawMsg)
60 + val, err := ctx.LoadModule("tls.certificates."+modName, rawMsg)
61 if err != nil {
62 return fmt.Errorf("loading certificate module '%s': %s", modName, err)
63 }
@@ -65,7 +68,7 @@ func (t *TLS) Provision() error {
68 }
69
70 // Start activates the TLS module.
68 -func (t *TLS) Start(handle caddy2.Handle) error {
71 +func (t *TLS) Start() error {
72 // load manual/static (unmanaged) certificates
73 for _, loader := range t.certificateLoaders {
74 certs, err := loader.LoadCertificates()
@@ -73,7 +76,7 @@ func (t *TLS) Start(handle caddy2.Handle) error {
76 return fmt.Errorf("loading certificates: %v", err)
77 }
78 magic := certmagic.New(t.certCache, certmagic.Config{
76 - Storage: caddy2.GetStorage(),
79 + Storage: t.ctx.Storage(),
80 })
81 for _, cert := range certs {
82 err := magic.CacheUnmanagedTLSCertificate(cert)
@@ -114,7 +117,7 @@ func (t *TLS) Stop() error {
117 func (t *TLS) Manage(names []string) error {
118 for _, name := range names {
119 ap := t.getAutomationPolicyForName(name)
117 - magic := certmagic.New(t.certCache, ap.makeCertMagicConfig())
120 + magic := certmagic.New(t.certCache, ap.makeCertMagicConfig(t.ctx))
121 err := magic.Manage([]string{name})
122 if err != nil {
123 return fmt.Errorf("automate: manage %s: %v", name, err)
@@ -130,13 +133,13 @@ func (t *TLS) HandleHTTPChallenge(w http.ResponseWriter, r *http.Request) bool {
133 return false
134 }
135 ap := t.getAutomationPolicyForName(r.Host)
133 - magic := certmagic.New(t.certCache, ap.makeCertMagicConfig())
136 + magic := certmagic.New(t.certCache, ap.makeCertMagicConfig(t.ctx))
137 return magic.HandleHTTPChallenge(w, r)
138 }
139
140 func (t *TLS) getConfigForName(name string) (certmagic.Config, error) {
141 ap := t.getAutomationPolicyForName(name)
139 - return ap.makeCertMagicConfig(), nil
142 + return ap.makeCertMagicConfig(t.ctx), nil
143 }
144
145 func (t *TLS) getAutomationPolicyForName(name string) AutomationPolicy {
@@ -178,12 +181,12 @@ type AutomationPolicy struct {
181 management ManagerMaker
182 }
183
181 -func (ap AutomationPolicy) makeCertMagicConfig() certmagic.Config {
184 +func (ap AutomationPolicy) makeCertMagicConfig(ctx caddy2.Context) certmagic.Config {
185 // default manager (ACME) is a special case because of how CertMagic is designed
186 // TODO: refactor certmagic so that ACME manager is not a special case by extracting
187 // its config fields out of the certmagic.Config struct, or something...
188 if acmeMgmt, ok := ap.management.(*acmeManagerMaker); ok {
186 - return acmeMgmt.makeCertMagicConfig()
189 + return acmeMgmt.makeCertMagicConfig(ctx)
190 }
191
192 return certmagic.Config{