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{