@marcel91200 / caddy Mirror · read-only
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 10, 22:30 UTC

master
go 382 lines 12.1 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddy
16
17 import (
18 "bytes"
19 "encoding/json"
20 "fmt"
21 "net/http"
22 "net/url"
23 "reflect"
24 "sort"
25 "strings"
26 "sync"
27 )
28
29 // Module is a type that is used as a Caddy module. In
30 // addition to this interface, most modules will implement
31 // some interface expected by their host module in order
32 // to be useful. To learn which interface(s) to implement,
33 // see the documentation for the host module. At a bare
34 // minimum, this interface, when implemented, only provides
35 // the module's ID and constructor function.
36 //
37 // Modules will often implement additional interfaces
38 // including Provisioner, Validator, and CleanerUpper.
39 // If a module implements these interfaces, their
40 // methods are called during the module's lifespan.
41 //
42 // When a module is loaded by a host module, the following
43 // happens: 1) ModuleInfo.New() is called to get a new
44 // instance of the module. 2) The module's configuration is
45 // unmarshaled into that instance. 3) If the module is a
46 // Provisioner, the Provision() method is called. 4) If the
47 // module is a Validator, the Validate() method is called.
48 // 5) The module will probably be type-asserted from
49 // 'any' to some other, more useful interface expected
50 // by the host module. For example, HTTP handler modules are
51 // type-asserted as caddyhttp.MiddlewareHandler values.
52 // 6) When a module's containing Context is canceled, if it is
53 // a CleanerUpper, its Cleanup() method is called.
54 type Module interface {
55 // This method indicates that the type is a Caddy
56 // module. The returned ModuleInfo must have both
57 // a name and a constructor function. This method
58 // must not have any side-effects.
59 CaddyModule() ModuleInfo
60 }
61
62 // ModuleInfo represents a registered Caddy module.
63 type ModuleInfo struct {
64 // ID is the "full name" of the module. It
65 // must be unique and properly namespaced.
66 ID ModuleID
67
68 // New returns a pointer to a new, empty
69 // instance of the module's type. This
70 // method must not have any side-effects,
71 // and no other initialization should
72 // occur within it. Any initialization
73 // of the returned value should be done
74 // in a Provision() method (see the
75 // Provisioner interface).
76 New func() Module
77 }
78
79 // ModuleID is a string that uniquely identifies a Caddy module. A
80 // module ID is lightly structured. It consists of dot-separated
81 // labels which form a simple hierarchy from left to right. The last
82 // label is the module name, and the labels before that constitute
83 // the namespace (or scope).
84 //
85 // Thus, a module ID has the form: <namespace>.<name>
86 //
87 // An ID with no dot has the empty namespace, which is appropriate
88 // for app modules (these are "top-level" modules that Caddy core
89 // loads and runs).
90 //
91 // Module IDs should be lowercase and use underscores (_) instead of
92 // spaces.
93 //
94 // Examples of valid IDs:
95 // - http
96 // - http.handlers.file_server
97 // - caddy.logging.encoders.json
98 type ModuleID string
99
100 // Namespace returns the namespace (or scope) portion of a module ID,
101 // which is all but the last label of the ID. If the ID has only one
102 // label, then the namespace is empty.
103 func (id ModuleID) Namespace() string {
104 lastDot := strings.LastIndex(string(id), ".")
105 if lastDot < 0 {
106 return ""
107 }
108 return string(id)[:lastDot]
109 }
110
111 // Name returns the Name (last element) of a module ID.
112 func (id ModuleID) Name() string {
113 if id == "" {
114 return ""
115 }
116 parts := strings.Split(string(id), ".")
117 return parts[len(parts)-1]
118 }
119
120 func (mi ModuleInfo) String() string { return string(mi.ID) }
121
122 // ModuleMap is a map that can contain multiple modules,
123 // where the map key is the module's name. (The namespace
124 // is usually read from an associated field's struct tag.)
125 // Because the module's name is given as the key in a
126 // module map, the name does not have to be given in the
127 // json.RawMessage.
128 type ModuleMap map[string]json.RawMessage
129
130 // RegisterModule registers a module by receiving a
131 // plain/empty value of the module. For registration to
132 // be properly recorded, this should be called in the
133 // init phase of runtime. Typically, the module package
134 // will do this as a side-effect of being imported.
135 // This function panics if the module's info is
136 // incomplete or invalid, or if the module is already
137 // registered.
138 func RegisterModule(instance Module) {
139 mod := instance.CaddyModule()
140
141 if mod.ID == "" {
142 panic("module ID missing")
143 }
144 if mod.ID == "caddy" || mod.ID == "admin" {
145 panic(fmt.Sprintf("module ID '%s' is reserved", mod.ID))
146 }
147 if mod.New == nil {
148 panic("missing ModuleInfo.New")
149 }
150 if val := mod.New(); val == nil {
151 panic("ModuleInfo.New must return a non-nil module instance")
152 }
153
154 modulesMu.Lock()
155 defer modulesMu.Unlock()
156
157 if _, ok := modules[string(mod.ID)]; ok {
158 panic(fmt.Sprintf("module already registered: %s", mod.ID))
159 }
160 modules[string(mod.ID)] = mod
161 }
162
163 // GetModule returns module information from its ID (full name).
164 func GetModule(name string) (ModuleInfo, error) {
165 modulesMu.RLock()
166 defer modulesMu.RUnlock()
167 m, ok := modules[name]
168 if !ok {
169 return ModuleInfo{}, fmt.Errorf("module not registered: %s", name)
170 }
171 return m, nil
172 }
173
174 // GetModuleName returns a module's name (the last label of its ID)
175 // from an instance of its value. If the value is not a module, an
176 // empty string will be returned.
177 func GetModuleName(instance any) string {
178 var name string
179 if mod, ok := instance.(Module); ok {
180 name = mod.CaddyModule().ID.Name()
181 }
182 return name
183 }
184
185 // GetModuleID returns a module's ID from an instance of its value.
186 // If the value is not a module, an empty string will be returned.
187 func GetModuleID(instance any) string {
188 var id string
189 if mod, ok := instance.(Module); ok {
190 id = string(mod.CaddyModule().ID)
191 }
192 return id
193 }
194
195 // GetModules returns all modules in the given scope/namespace.
196 // For example, a scope of "foo" returns modules named "foo.bar",
197 // "foo.loo", but not "bar", "foo.bar.loo", etc. An empty scope
198 // returns top-level modules, for example "foo" or "bar". Partial
199 // scopes are not matched (i.e. scope "foo.ba" does not match
200 // name "foo.bar").
201 //
202 // Because modules are registered to a map under the hood, the
203 // returned slice will be sorted to keep it deterministic.
204 func GetModules(scope string) []ModuleInfo {
205 modulesMu.RLock()
206 defer modulesMu.RUnlock()
207
208 scopeParts := strings.Split(scope, ".")
209
210 // handle the special case of an empty scope, which
211 // should match only the top-level modules
212 if scope == "" {
213 scopeParts = []string{}
214 }
215
216 var mods []ModuleInfo
217 iterateModules:
218 for id, m := range modules {
219 modParts := strings.Split(id, ".")
220
221 // match only the next level of nesting
222 if len(modParts) != len(scopeParts)+1 {
223 continue
224 }
225
226 // specified parts must be exact matches
227 for i := range scopeParts {
228 if modParts[i] != scopeParts[i] {
229 continue iterateModules
230 }
231 }
232
233 mods = append(mods, m)
234 }
235
236 // make return value deterministic
237 sort.Slice(mods, func(i, j int) bool {
238 return mods[i].ID < mods[j].ID
239 })
240
241 return mods
242 }
243
244 // Modules returns the names of all registered modules
245 // in ascending lexicographical order.
246 func Modules() []string {
247 modulesMu.RLock()
248 defer modulesMu.RUnlock()
249
250 names := make([]string, 0, len(modules))
251 for name := range modules {
252 names = append(names, name)
253 }
254
255 sort.Strings(names)
256
257 return names
258 }
259
260 // getModuleNameInline loads the string value from raw of moduleNameKey,
261 // where raw must be a JSON encoding of a map. It returns that value,
262 // along with the result of removing that key from raw.
263 func getModuleNameInline(moduleNameKey string, raw json.RawMessage) (string, json.RawMessage, error) {
264 var tmp map[string]any
265 err := json.Unmarshal(raw, &tmp)
266 if err != nil {
267 return "", nil, err
268 }
269
270 moduleName, ok := tmp[moduleNameKey].(string)
271 if !ok || moduleName == "" {
272 return "", nil, fmt.Errorf("module name not specified with key '%s' in %+v", moduleNameKey, tmp)
273 }
274
275 // remove key from the object, otherwise decoding it later
276 // will yield an error because the struct won't recognize it
277 // (this is only needed because we strictly enforce that
278 // all keys are recognized when loading modules)
279 delete(tmp, moduleNameKey)
280 result, err := json.Marshal(tmp)
281 if err != nil {
282 return "", nil, fmt.Errorf("re-encoding module configuration: %v", err)
283 }
284
285 return moduleName, result, nil
286 }
287
288 // Provisioner is implemented by modules which may need to perform
289 // some additional "setup" steps immediately after being loaded.
290 // Provisioning should be fast (imperceptible running time). If
291 // any side-effects result in the execution of this function (e.g.
292 // creating global state, any other allocations which require
293 // garbage collection, opening files, starting goroutines etc.),
294 // be sure to clean up properly by implementing the CleanerUpper
295 // interface to avoid leaking resources.
296 type Provisioner interface {
297 Provision(Context) error
298 }
299
300 // Validator is implemented by modules which can verify that their
301 // configurations are valid. This method will be called after
302 // Provision() (if implemented). Validation should always be fast
303 // (imperceptible running time) and an error must be returned if
304 // the module's configuration is invalid.
305 type Validator interface {
306 Validate() error
307 }
308
309 // CleanerUpper is implemented by modules which may have side-effects
310 // such as opened files, spawned goroutines, or allocated some sort
311 // of non-stack state when they were provisioned. This method should
312 // deallocate/cleanup those resources to prevent memory leaks. Cleanup
313 // should be fast and efficient. Cleanup should work even if Provision
314 // returns an error, to allow cleaning up from partial provisionings.
315 type CleanerUpper interface {
316 Cleanup() error
317 }
318
319 // ParseStructTag parses a caddy struct tag into its keys and values.
320 // It is very simple. The expected syntax is:
321 // `caddy:"key1=val1 key2=val2 ..."`
322 func ParseStructTag(tag string) (map[string]string, error) {
323 results := make(map[string]string)
324 pairs := strings.Split(tag, " ")
325 for i, pair := range pairs {
326 if pair == "" {
327 continue
328 }
329 before, after, isCut := strings.Cut(pair, "=")
330 if !isCut {
331 return nil, fmt.Errorf("missing key in '%s' (pair %d)", pair, i)
332 }
333 results[before] = after
334 }
335 return results, nil
336 }
337
338 // StrictUnmarshalJSON is like json.Unmarshal but returns an error
339 // if any of the fields are unrecognized. Useful when decoding
340 // module configurations, where you want to be more sure they're
341 // correct.
342 func StrictUnmarshalJSON(data []byte, v any) error {
343 dec := json.NewDecoder(bytes.NewReader(data))
344 dec.DisallowUnknownFields()
345 err := dec.Decode(v)
346 if jsonErr, ok := err.(*json.SyntaxError); ok {
347 return fmt.Errorf("%w, at offset %d", jsonErr, jsonErr.Offset)
348 }
349 return err
350 }
351
352 var JSONRawMessageType = reflect.TypeFor[json.RawMessage]()
353
354 // isJSONRawMessage returns true if the type is encoding/json.RawMessage.
355 func isJSONRawMessage(typ reflect.Type) bool {
356 return typ == JSONRawMessageType
357 }
358
359 // isModuleMapType returns true if the type is map[string]json.RawMessage.
360 // It assumes that the string key is the module name, but this is not
361 // always the case. To know for sure, this function must return true, but
362 // also the struct tag where this type appears must NOT define an inline_key
363 // attribute, which would mean that the module names appear inline with the
364 // values, not in the key.
365 func isModuleMapType(typ reflect.Type) bool {
366 return typ.Kind() == reflect.Map &&
367 typ.Key().Kind() == reflect.String &&
368 isJSONRawMessage(typ.Elem())
369 }
370
371 // ProxyFuncProducer is implemented by modules which produce a
372 // function that returns a URL to use as network proxy. Modules
373 // in the namespace `caddy.network_proxy` must implement this
374 // interface.
375 type ProxyFuncProducer interface {
376 ProxyFunc() func(*http.Request) (*url.URL, error)
377 }
378
379 var (
380 modules = make(map[string]ModuleInfo)
381 modulesMu sync.RWMutex
382 )