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
| 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 | ) |