| 
									
										
										
										
											2019-06-30 16:07:58 -06:00
										 |  |  | // Copyright 2015 Matthew Holt and The Caddy Authors | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License. | 
					
						
							|  |  |  | // You may obtain a copy of the License at | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and | 
					
						
							|  |  |  | // limitations under the License. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-14 11:58:28 -06:00
										 |  |  | package caddy | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"log" | 
					
						
							| 
									
										
										
										
											2024-01-25 14:31:15 -05:00
										 |  |  | 	"log/slog" | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	"reflect" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-06 23:15:25 -07:00
										 |  |  | 	"github.com/caddyserver/certmagic" | 
					
						
							| 
									
										
										
										
											2024-10-02 17:23:26 +03:00
										 |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							|  |  |  | 	"github.com/prometheus/client_golang/prometheus/collectors" | 
					
						
							| 
									
										
										
										
											2019-10-28 14:39:37 -06:00
										 |  |  | 	"go.uber.org/zap" | 
					
						
							| 
									
										
										
										
											2024-01-25 14:31:15 -05:00
										 |  |  | 	"go.uber.org/zap/exp/zapslog" | 
					
						
							| 
									
										
										
										
											2024-01-13 14:12:43 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/caddyserver/caddy/v2/internal/filesystems" | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-17 08:48:12 -06:00
										 |  |  | // Context is a type which defines the lifetime of modules that | 
					
						
							|  |  |  | // are loaded and provides access to the parent configuration | 
					
						
							|  |  |  | // that spawned the modules which are loaded. It should be used | 
					
						
							| 
									
										
										
										
											2019-06-26 16:03:29 -06:00
										 |  |  | // with care and wrapped with derivation functions from the | 
					
						
							|  |  |  | // standard context package only if you don't need the Caddy | 
					
						
							| 
									
										
										
										
											2020-01-03 21:33:22 +03:00
										 |  |  | // specific features. These contexts are canceled when the | 
					
						
							| 
									
										
										
										
											2019-10-10 15:38:30 -06:00
										 |  |  | // lifetime of the modules loaded from it is over. | 
					
						
							| 
									
										
										
										
											2019-05-17 08:48:12 -06:00
										 |  |  | // | 
					
						
							|  |  |  | // Use NewContext() to get a valid value (but most modules will | 
					
						
							|  |  |  | // not actually need to do this). | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | type Context struct { | 
					
						
							|  |  |  | 	context.Context | 
					
						
							| 
									
										
										
										
											2024-01-13 14:12:43 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 	moduleInstances map[string][]Module | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	cfg             *Config | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 	ancestry        []Module | 
					
						
							| 
									
										
										
										
											2024-03-01 09:57:05 -07:00
										 |  |  | 	cleanupFuncs    []func()                // invoked at every config unload | 
					
						
							|  |  |  | 	exitFuncs       []func(context.Context) // invoked at config unload ONLY IF the process is exiting (EXPERIMENTAL) | 
					
						
							| 
									
										
										
										
											2024-10-02 17:23:26 +03:00
										 |  |  | 	metricsRegistry *prometheus.Registry | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-17 08:48:12 -06:00
										 |  |  | // NewContext provides a new context derived from the given | 
					
						
							|  |  |  | // context ctx. Normally, you will not need to call this | 
					
						
							|  |  |  | // function unless you are loading modules which have a | 
					
						
							|  |  |  | // different lifespan than the ones for the context the | 
					
						
							|  |  |  | // module was provisioned with. Be sure to call the cancel | 
					
						
							|  |  |  | // func when the context is to be cleaned up so that | 
					
						
							|  |  |  | // modules which are loaded will be properly unloaded. | 
					
						
							|  |  |  | // See standard library context package's documentation. | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | func NewContext(ctx Context) (Context, context.CancelFunc) { | 
					
						
							| 
									
										
										
										
											2024-10-02 17:23:26 +03:00
										 |  |  | 	newCtx := Context{moduleInstances: make(map[string][]Module), cfg: ctx.cfg, metricsRegistry: prometheus.NewPedanticRegistry()} | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	c, cancel := context.WithCancel(ctx.Context) | 
					
						
							|  |  |  | 	wrappedCancel := func() { | 
					
						
							|  |  |  | 		cancel() | 
					
						
							| 
									
										
										
										
											2019-05-14 10:35:41 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 23:10:12 -06:00
										 |  |  | 		for _, f := range ctx.cleanupFuncs { | 
					
						
							|  |  |  | 			f() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		for modName, modInstances := range newCtx.moduleInstances { | 
					
						
							|  |  |  | 			for _, inst := range modInstances { | 
					
						
							|  |  |  | 				if cu, ok := inst.(CleanerUpper); ok { | 
					
						
							|  |  |  | 					err := cu.Cleanup() | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						log.Printf("[ERROR] %s (%p): cleanup: %v", modName, inst, err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	newCtx.Context = c | 
					
						
							| 
									
										
										
										
											2024-10-02 17:23:26 +03:00
										 |  |  | 	newCtx.initMetrics() | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	return newCtx, wrappedCancel | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-03 21:33:22 +03:00
										 |  |  | // OnCancel executes f when ctx is canceled. | 
					
						
							| 
									
										
										
										
											2019-05-29 23:10:12 -06:00
										 |  |  | func (ctx *Context) OnCancel(f func()) { | 
					
						
							|  |  |  | 	ctx.cleanupFuncs = append(ctx.cleanupFuncs, f) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-29 08:15:43 -06:00
										 |  |  | // FileSystems returns a ref to the FilesystemMap. | 
					
						
							| 
									
										
										
										
											2024-03-01 09:57:05 -07:00
										 |  |  | // EXPERIMENTAL: This API is subject to change. | 
					
						
							| 
									
										
										
										
											2025-03-29 08:15:43 -06:00
										 |  |  | func (ctx *Context) FileSystems() FileSystems { | 
					
						
							| 
									
										
										
										
											2024-01-13 14:12:43 -06:00
										 |  |  | 	// if no config is loaded, we use a default filesystemmap, which includes the osfs | 
					
						
							|  |  |  | 	if ctx.cfg == nil { | 
					
						
							| 
									
										
										
										
											2025-03-29 08:15:43 -06:00
										 |  |  | 		return &filesystems.FileSystemMap{} | 
					
						
							| 
									
										
										
										
											2024-01-13 14:12:43 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-03-29 08:15:43 -06:00
										 |  |  | 	return ctx.cfg.fileSystems | 
					
						
							| 
									
										
										
										
											2024-01-13 14:12:43 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-02 17:23:26 +03:00
										 |  |  | // Returns the active metrics registry for the context | 
					
						
							|  |  |  | // EXPERIMENTAL: This API is subject to change. | 
					
						
							|  |  |  | func (ctx *Context) GetMetricsRegistry() *prometheus.Registry { | 
					
						
							|  |  |  | 	return ctx.metricsRegistry | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ctx *Context) initMetrics() { | 
					
						
							|  |  |  | 	ctx.metricsRegistry.MustRegister( | 
					
						
							|  |  |  | 		collectors.NewBuildInfoCollector(), | 
					
						
							| 
									
										
										
										
											2024-11-23 02:38:59 +03:00
										 |  |  | 		collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), | 
					
						
							|  |  |  | 		collectors.NewGoCollector(), | 
					
						
							| 
									
										
										
										
											2024-10-02 17:23:26 +03:00
										 |  |  | 		adminMetrics.requestCount, | 
					
						
							|  |  |  | 		adminMetrics.requestErrors, | 
					
						
							|  |  |  | 		globalMetrics.configSuccess, | 
					
						
							|  |  |  | 		globalMetrics.configSuccessTime, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-01 09:57:05 -07:00
										 |  |  | // OnExit executes f when the process exits gracefully. | 
					
						
							|  |  |  | // The function is only executed if the process is gracefully | 
					
						
							|  |  |  | // shut down while this context is active. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // EXPERIMENTAL API: subject to change or removal. | 
					
						
							|  |  |  | func (ctx *Context) OnExit(f func(context.Context)) { | 
					
						
							|  |  |  | 	ctx.exitFuncs = append(ctx.exitFuncs, f) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | // LoadModule loads the Caddy module(s) from the specified field of the parent struct | 
					
						
							|  |  |  | // pointer and returns the loaded module(s). The struct pointer and its field name as | 
					
						
							|  |  |  | // a string are necessary so that reflection can be used to read the struct tag on the | 
					
						
							|  |  |  | // field to get the module namespace and inline module name key (if specified). | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The field can be any one of the supported raw module types: json.RawMessage, | 
					
						
							|  |  |  | // []json.RawMessage, map[string]json.RawMessage, or []map[string]json.RawMessage. | 
					
						
							|  |  |  | // ModuleMap may be used in place of map[string]json.RawMessage. The return value's | 
					
						
							|  |  |  | // underlying type mirrors the input field's type: | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | //	json.RawMessage              => any | 
					
						
							|  |  |  | //	[]json.RawMessage            => []any | 
					
						
							|  |  |  | //	[][]json.RawMessage          => [][]any | 
					
						
							|  |  |  | //	map[string]json.RawMessage   => map[string]any | 
					
						
							|  |  |  | //	[]map[string]json.RawMessage => []map[string]any | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | // | 
					
						
							|  |  |  | // The field must have a "caddy" struct tag in this format: | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | //	caddy:"key1=val1 key2=val2" | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | // | 
					
						
							|  |  |  | // To load modules, a "namespace" key is required. For example, to load modules | 
					
						
							|  |  |  | // in the "http.handlers" namespace, you'd put: `namespace=http.handlers` in the | 
					
						
							|  |  |  | // Caddy struct tag. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The module name must also be available. If the field type is a map or slice of maps, | 
					
						
							|  |  |  | // then key is assumed to be the module name if an "inline_key" is NOT specified in the | 
					
						
							|  |  |  | // caddy struct tag. In this case, the module name does NOT need to be specified in-line | 
					
						
							|  |  |  | // with the module itself. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // If not a map, or if inline_key is non-empty, then the module name must be embedded | 
					
						
							|  |  |  | // into the values, which must be objects; then there must be a key in those objects | 
					
						
							|  |  |  | // where its associated value is the module name. This is called the "inline key", | 
					
						
							|  |  |  | // meaning the key containing the module's name that is defined inline with the module | 
					
						
							|  |  |  | // itself. You must specify the inline key in a struct tag, along with the namespace: | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | //	caddy:"namespace=http.handlers inline_key=handler" | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | // | 
					
						
							|  |  |  | // This will look for a key/value pair like `"handler": "..."` in the json.RawMessage | 
					
						
							|  |  |  | // in order to know the module name. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // To make use of the loaded module(s) (the return value), you will probably want | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | // to type-assert each 'any' value(s) to the types that are useful to you | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | // and store them on the same struct. Storing them on the same struct makes for | 
					
						
							|  |  |  | // easy garbage collection when your host module is no longer needed. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2019-12-10 14:06:35 -07:00
										 |  |  | // Loaded modules have already been provisioned and validated. Upon returning | 
					
						
							|  |  |  | // successfully, this method clears the json.RawMessage(s) in the field since | 
					
						
							|  |  |  | // the raw JSON is no longer needed, and this allows the GC to free up memory. | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | func (ctx Context) LoadModule(structPointer any, fieldName string) (any, error) { | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	val := reflect.ValueOf(structPointer).Elem().FieldByName(fieldName) | 
					
						
							|  |  |  | 	typ := val.Type() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	field, ok := reflect.TypeOf(structPointer).Elem().FieldByName(fieldName) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		panic(fmt.Sprintf("field %s does not exist in %#v", fieldName, structPointer)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts, err := ParseStructTag(field.Tag.Get("caddy")) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		panic(fmt.Sprintf("malformed tag on field %s: %v", fieldName, err)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	moduleNamespace, ok := opts["namespace"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		panic(fmt.Sprintf("missing 'namespace' key in struct tag on field %s", fieldName)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	inlineModuleKey := opts["inline_key"] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | 	var result any | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch val.Kind() { | 
					
						
							|  |  |  | 	case reflect.Slice: | 
					
						
							|  |  |  | 		if isJSONRawMessage(typ) { | 
					
						
							|  |  |  | 			// val is `json.RawMessage` ([]uint8 under the hood) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if inlineModuleKey == "" { | 
					
						
							|  |  |  | 				panic("unable to determine module name without inline_key when type is not a ModuleMap") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			val, err := ctx.loadModuleInline(inlineModuleKey, moduleNamespace, val.Interface().(json.RawMessage)) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			result = val | 
					
						
							|  |  |  | 		} else if isJSONRawMessage(typ.Elem()) { | 
					
						
							|  |  |  | 			// val is `[]json.RawMessage` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if inlineModuleKey == "" { | 
					
						
							|  |  |  | 				panic("unable to determine module name without inline_key because type is not a ModuleMap") | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | 			var all []any | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 			for i := 0; i < val.Len(); i++ { | 
					
						
							|  |  |  | 				val, err := ctx.loadModuleInline(inlineModuleKey, moduleNamespace, val.Index(i).Interface().(json.RawMessage)) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return nil, fmt.Errorf("position %d: %v", i, err) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				all = append(all, val) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			result = all | 
					
						
							| 
									
										
										
										
											2020-05-06 13:18:56 -06:00
										 |  |  | 		} else if typ.Elem().Kind() == reflect.Slice && isJSONRawMessage(typ.Elem().Elem()) { | 
					
						
							|  |  |  | 			// val is `[][]json.RawMessage` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if inlineModuleKey == "" { | 
					
						
							|  |  |  | 				panic("unable to determine module name without inline_key because type is not a ModuleMap") | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | 			var all [][]any | 
					
						
							| 
									
										
										
										
											2020-05-06 13:18:56 -06:00
										 |  |  | 			for i := 0; i < val.Len(); i++ { | 
					
						
							|  |  |  | 				innerVal := val.Index(i) | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | 				var allInner []any | 
					
						
							| 
									
										
										
										
											2020-05-06 13:18:56 -06:00
										 |  |  | 				for j := 0; j < innerVal.Len(); j++ { | 
					
						
							|  |  |  | 					innerInnerVal, err := ctx.loadModuleInline(inlineModuleKey, moduleNamespace, innerVal.Index(j).Interface().(json.RawMessage)) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, fmt.Errorf("position %d: %v", j, err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					allInner = append(allInner, innerInnerVal) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				all = append(all, allInner) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			result = all | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		} else if isModuleMapType(typ.Elem()) { | 
					
						
							|  |  |  | 			// val is `[]map[string]json.RawMessage` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | 			var all []map[string]any | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 			for i := 0; i < val.Len(); i++ { | 
					
						
							|  |  |  | 				thisSet, err := ctx.loadModulesFromSomeMap(moduleNamespace, inlineModuleKey, val.Index(i)) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return nil, err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				all = append(all, thisSet) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			result = all | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case reflect.Map: | 
					
						
							|  |  |  | 		// val is a ModuleMap or some other kind of map | 
					
						
							|  |  |  | 		result, err = ctx.loadModulesFromSomeMap(moduleNamespace, inlineModuleKey, val) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("unrecognized type for module: %s", typ) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// we're done with the raw bytes; allow GC to deallocate | 
					
						
							|  |  |  | 	val.Set(reflect.Zero(typ)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return result, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-29 08:15:43 -06:00
										 |  |  | // emitEvent is a small convenience method so the caddy core can emit events, if the event app is configured. | 
					
						
							|  |  |  | func (ctx Context) emitEvent(name string, data map[string]any) Event { | 
					
						
							|  |  |  | 	if ctx.cfg == nil || ctx.cfg.eventEmitter == nil { | 
					
						
							|  |  |  | 		return Event{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ctx.cfg.eventEmitter.Emit(ctx, name, data) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | // loadModulesFromSomeMap loads modules from val, which must be a type of map[string]any. | 
					
						
							| 
									
										
										
										
											2020-02-28 10:30:48 +08:00
										 |  |  | // Depending on inlineModuleKey, it will be interpreted as either a ModuleMap (key is the module | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | // name) or as a regular map (key is not the module name, and module name is defined inline). | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | func (ctx Context) loadModulesFromSomeMap(namespace, inlineModuleKey string, val reflect.Value) (map[string]any, error) { | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	// if no inline_key is specified, then val must be a ModuleMap, | 
					
						
							|  |  |  | 	// where the key is the module name | 
					
						
							|  |  |  | 	if inlineModuleKey == "" { | 
					
						
							|  |  |  | 		if !isModuleMapType(val.Type()) { | 
					
						
							|  |  |  | 			panic(fmt.Sprintf("expected ModuleMap because inline_key is empty; but we do not recognize this type: %s", val.Type())) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return ctx.loadModuleMap(namespace, val) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// otherwise, val is a map with modules, but the module name is | 
					
						
							|  |  |  | 	// inline with each value (the key means something else) | 
					
						
							|  |  |  | 	return ctx.loadModulesFromRegularMap(namespace, inlineModuleKey, val) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // loadModulesFromRegularMap loads modules from val, where val is a map[string]json.RawMessage. | 
					
						
							|  |  |  | // Map keys are NOT interpreted as module names, so module names are still expected to appear | 
					
						
							|  |  |  | // inline with the objects. | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | func (ctx Context) loadModulesFromRegularMap(namespace, inlineModuleKey string, val reflect.Value) (map[string]any, error) { | 
					
						
							|  |  |  | 	mods := make(map[string]any) | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	iter := val.MapRange() | 
					
						
							|  |  |  | 	for iter.Next() { | 
					
						
							|  |  |  | 		k := iter.Key() | 
					
						
							|  |  |  | 		v := iter.Value() | 
					
						
							|  |  |  | 		mod, err := ctx.loadModuleInline(inlineModuleKey, namespace, v.Interface().(json.RawMessage)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("key %s: %v", k, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		mods[k.String()] = mod | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return mods, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | // loadModuleMap loads modules from a ModuleMap, i.e. map[string]any, where the key is the | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | // module name. With a module map, module names do not need to be defined inline with their values. | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | func (ctx Context) loadModuleMap(namespace string, val reflect.Value) (map[string]any, error) { | 
					
						
							|  |  |  | 	all := make(map[string]any) | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	iter := val.MapRange() | 
					
						
							|  |  |  | 	for iter.Next() { | 
					
						
							|  |  |  | 		k := iter.Key().Interface().(string) | 
					
						
							|  |  |  | 		v := iter.Value().Interface().(json.RawMessage) | 
					
						
							|  |  |  | 		moduleName := namespace + "." + k | 
					
						
							|  |  |  | 		if namespace == "" { | 
					
						
							|  |  |  | 			moduleName = k | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		val, err := ctx.LoadModuleByID(moduleName, v) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("module name '%s': %v", k, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		all[k] = val | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return all, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LoadModuleByID decodes rawMsg into a new instance of mod and | 
					
						
							|  |  |  | // returns the value. If mod.New is nil, an error is returned. | 
					
						
							|  |  |  | // If the module implements Validator or Provisioner interfaces, | 
					
						
							|  |  |  | // those methods are invoked to ensure the module is fully | 
					
						
							|  |  |  | // configured and valid before being used. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This is a lower-level method and will usually not be called | 
					
						
							|  |  |  | // directly by most modules. However, this method is useful when | 
					
						
							|  |  |  | // dynamically loading/unloading modules in their own context, | 
					
						
							|  |  |  | // like from embedded scripts, etc. | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error) { | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	modulesMu.RLock() | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 	modInfo, ok := modules[id] | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	modulesMu.RUnlock() | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		return nil, fmt.Errorf("unknown module: %s", id) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 	if modInfo.New == nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("module '%s' has no constructor", modInfo.ID) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 	val := modInfo.New() | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	// value must be a pointer for unmarshaling into concrete type, even if | 
					
						
							|  |  |  | 	// the module's concrete type is a slice or map; New() *should* return | 
					
						
							|  |  |  | 	// a pointer, otherwise unmarshaling errors or panics will occur | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	if rv := reflect.ValueOf(val); rv.Kind() != reflect.Ptr { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 		log.Printf("[WARNING] ModuleInfo.New() for module '%s' did not return a pointer,"+ | 
					
						
							|  |  |  | 			" so we are using reflection to make a pointer instead; please fix this by"+ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 			" using new(Type) or &Type notation in your module's New() function.", id) | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 		val = reflect.New(rv.Type()).Elem().Addr().Interface().(Module) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// fill in its config only if there is a config to fill in | 
					
						
							|  |  |  | 	if len(rawMsg) > 0 { | 
					
						
							| 
									
										
										
										
											2023-02-22 13:39:40 -05:00
										 |  |  | 		err := StrictUnmarshalJSON(rawMsg, &val) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 			return nil, fmt.Errorf("decoding module config: %s: %v", modInfo, err) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-26 10:45:34 -06:00
										 |  |  | 	if val == nil { | 
					
						
							|  |  |  | 		// returned module values are almost always type-asserted | 
					
						
							|  |  |  | 		// before being used, so a nil value would panic; and there | 
					
						
							|  |  |  | 		// is no good reason to explicitly declare null modules in | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		// a config; it might be because the user is trying to achieve | 
					
						
							|  |  |  | 		// a result the developer isn't expecting, which is a smell | 
					
						
							| 
									
										
										
										
											2019-06-26 10:45:34 -06:00
										 |  |  | 		return nil, fmt.Errorf("module value cannot be null") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-05 17:04:10 -07:00
										 |  |  | 	// if this is an app module, keep a reference to it, | 
					
						
							|  |  |  | 	// since submodules may need to reference it during | 
					
						
							|  |  |  | 	// provisioning (even though the parent app module | 
					
						
							|  |  |  | 	// may not be fully provisioned yet; this is the case | 
					
						
							|  |  |  | 	// with the tls app's automation policies, which may | 
					
						
							|  |  |  | 	// refer to the tls app to check if a global DNS | 
					
						
							|  |  |  | 	// module has been configured for DNS challenges) | 
					
						
							|  |  |  | 	if appModule, ok := val.(App); ok { | 
					
						
							|  |  |  | 		ctx.cfg.apps[id] = appModule | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 	ctx.ancestry = append(ctx.ancestry, val) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	if prov, ok := val.(Provisioner); ok { | 
					
						
							|  |  |  | 		err := prov.Provision(ctx) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-09-30 09:16:01 -06:00
										 |  |  | 			// incomplete provisioning could have left state | 
					
						
							|  |  |  | 			// dangling, so make sure it gets cleaned up | 
					
						
							|  |  |  | 			if cleanerUpper, ok := val.(CleanerUpper); ok { | 
					
						
							|  |  |  | 				err2 := cleanerUpper.Cleanup() | 
					
						
							|  |  |  | 				if err2 != nil { | 
					
						
							|  |  |  | 					err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 			return nil, fmt.Errorf("provision %s: %v", modInfo, err) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if validator, ok := val.(Validator); ok { | 
					
						
							| 
									
										
										
										
											2019-05-17 08:48:12 -06:00
										 |  |  | 		err := validator.Validate() | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-09-30 09:16:01 -06:00
										 |  |  | 			// since the module was already provisioned, make sure we clean up | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 			if cleanerUpper, ok := val.(CleanerUpper); ok { | 
					
						
							|  |  |  | 				err2 := cleanerUpper.Cleanup() | 
					
						
							|  |  |  | 				if err2 != nil { | 
					
						
							|  |  |  | 					err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 			return nil, fmt.Errorf("%s: invalid configuration: %v", modInfo, err) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	ctx.moduleInstances[id] = append(ctx.moduleInstances[id], val) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-29 08:15:43 -06:00
										 |  |  | 	// if the loaded module happens to be an app that can emit events, store it so the | 
					
						
							|  |  |  | 	// core can have access to emit events without an import cycle | 
					
						
							|  |  |  | 	if ee, ok := val.(eventEmitter); ok { | 
					
						
							|  |  |  | 		if _, ok := ee.(App); ok { | 
					
						
							|  |  |  | 			ctx.cfg.eventEmitter = ee | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	return val, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | // loadModuleInline loads a module from a JSON raw message which decodes to | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | // a map[string]any, where one of the object keys is moduleNameKey | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | // and the corresponding value is the module name (as a string) which can | 
					
						
							|  |  |  | // be found in the given scope. In other words, the module name is declared | 
					
						
							|  |  |  | // in-line with the module itself. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This allows modules to be decoded into their concrete types and used when | 
					
						
							|  |  |  | // their names cannot be the unique key in a map, such as when there are | 
					
						
							|  |  |  | // multiple instances in the map or it appears in an array (where there are | 
					
						
							|  |  |  | // no custom keys). In other words, the key containing the module name is | 
					
						
							|  |  |  | // treated special/separate from all the other keys in the object. | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | func (ctx Context) loadModuleInline(moduleNameKey, moduleScope string, raw json.RawMessage) (any, error) { | 
					
						
							| 
									
										
										
										
											2019-05-22 14:32:12 -06:00
										 |  |  | 	moduleName, raw, err := getModuleNameInline(moduleNameKey, raw) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	val, err := ctx.LoadModuleByID(moduleScope+"."+moduleName, raw) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("loading module '%s': %v", moduleName, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return val, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 20:25:46 -06:00
										 |  |  | // App returns the configured app named name. If that app has | 
					
						
							|  |  |  | // not yet been loaded and provisioned, it will be immediately | 
					
						
							|  |  |  | // loaded and provisioned. If no app with that name is | 
					
						
							|  |  |  | // configured, a new empty one will be instantiated instead. | 
					
						
							|  |  |  | // (The app module must still be registered.) This must not be | 
					
						
							|  |  |  | // called during the Provision/Validate phase to reference a | 
					
						
							|  |  |  | // module's own host app (since the parent app module is still | 
					
						
							|  |  |  | // in the process of being provisioned, it is not yet ready). | 
					
						
							| 
									
										
										
										
											2023-07-11 13:10:58 -06:00
										 |  |  | // | 
					
						
							|  |  |  | // We return any type instead of the App type because it is NOT | 
					
						
							|  |  |  | // intended for the caller of this method to be the one to start | 
					
						
							|  |  |  | // or stop App modules. The caller is expected to assert to the | 
					
						
							|  |  |  | // concrete type. | 
					
						
							| 
									
										
										
										
											2022-08-02 16:39:09 -04:00
										 |  |  | func (ctx Context) App(name string) (any, error) { | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	if app, ok := ctx.cfg.apps[name]; ok { | 
					
						
							|  |  |  | 		return app, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-06 23:15:25 -07:00
										 |  |  | 	appRaw := ctx.cfg.AppsRaw[name] | 
					
						
							|  |  |  | 	modVal, err := ctx.LoadModuleByID(name, appRaw) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-03-06 23:15:25 -07:00
										 |  |  | 		return nil, fmt.Errorf("loading %s app module: %v", name, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if appRaw != nil { | 
					
						
							|  |  |  | 		ctx.cfg.AppsRaw[name] = nil // allow GC to deallocate | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return modVal, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-20 13:14:58 -04:00
										 |  |  | // AppIfConfigured is like App, but it returns an error if the | 
					
						
							|  |  |  | // app has not been configured. This is useful when the app is | 
					
						
							| 
									
										
										
										
											2024-05-22 18:47:03 -06:00
										 |  |  | // required and its absence is a configuration error; or when | 
					
						
							| 
									
										
										
										
											2024-05-20 13:14:58 -04:00
										 |  |  | // the app is optional and you don't want to instantiate a | 
					
						
							| 
									
										
										
										
											2024-05-22 18:47:03 -06:00
										 |  |  | // new one that hasn't been explicitly configured. If the app | 
					
						
							|  |  |  | // is not in the configuration, the error wraps ErrNotConfigured. | 
					
						
							| 
									
										
										
										
											2024-05-20 13:14:58 -04:00
										 |  |  | func (ctx Context) AppIfConfigured(name string) (any, error) { | 
					
						
							| 
									
										
										
										
											2024-05-22 18:47:03 -06:00
										 |  |  | 	if ctx.cfg == nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-05-20 13:14:58 -04:00
										 |  |  | 	if app, ok := ctx.cfg.apps[name]; ok { | 
					
						
							|  |  |  | 		return app, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	appRaw := ctx.cfg.AppsRaw[name] | 
					
						
							|  |  |  | 	if appRaw == nil { | 
					
						
							| 
									
										
										
										
											2024-05-22 18:47:03 -06:00
										 |  |  | 		return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured) | 
					
						
							| 
									
										
										
										
											2023-07-21 15:32:20 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-05-20 13:14:58 -04:00
										 |  |  | 	return ctx.App(name) | 
					
						
							| 
									
										
										
										
											2022-03-02 13:08:36 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-22 18:47:03 -06:00
										 |  |  | // ErrNotConfigured indicates a module is not configured. | 
					
						
							|  |  |  | var ErrNotConfigured = fmt.Errorf("module not configured") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | // Storage returns the configured Caddy storage implementation. | 
					
						
							|  |  |  | func (ctx Context) Storage() certmagic.Storage { | 
					
						
							|  |  |  | 	return ctx.cfg.storage | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-28 14:39:37 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 16:55:30 -06:00
										 |  |  | // Logger returns a logger that is intended for use by the most | 
					
						
							|  |  |  | // recent module associated with the context. Callers should not | 
					
						
							|  |  |  | // pass in any arguments unless they want to associate with a | 
					
						
							|  |  |  | // different module; it panics if more than 1 value is passed in. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Originally, this method's signature was `Logger(mod Module)`, | 
					
						
							| 
									
										
										
										
											2022-10-05 21:36:06 +03:00
										 |  |  | // requiring that an instance of a Caddy module be passed in. | 
					
						
							| 
									
										
										
										
											2022-09-16 16:55:30 -06:00
										 |  |  | // However, that is no longer necessary, as the closest module | 
					
						
							|  |  |  | // most recently associated with the context will be automatically | 
					
						
							|  |  |  | // assumed. To prevent a sudden breaking change, this method's | 
					
						
							|  |  |  | // signature has been changed to be variadic, but we may remove | 
					
						
							|  |  |  | // the parameter altogether in the future. Callers should not | 
					
						
							|  |  |  | // pass in any argument. If there is valid need to specify a | 
					
						
							|  |  |  | // different module, please open an issue to discuss. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // PARTIALLY DEPRECATED: The Logger(module) form is deprecated and | 
					
						
							|  |  |  | // may be removed in the future. Do not pass in any arguments. | 
					
						
							|  |  |  | func (ctx Context) Logger(module ...Module) *zap.Logger { | 
					
						
							|  |  |  | 	if len(module) > 1 { | 
					
						
							|  |  |  | 		panic("more than 1 module passed in") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-30 10:14:52 -06:00
										 |  |  | 	if ctx.cfg == nil { | 
					
						
							|  |  |  | 		// often the case in tests; just use a dev logger | 
					
						
							|  |  |  | 		l, err := zap.NewDevelopment() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic("config missing, unable to create dev logger: " + err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return l | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-09-16 16:55:30 -06:00
										 |  |  | 	mod := ctx.Module() | 
					
						
							|  |  |  | 	if len(module) > 0 { | 
					
						
							|  |  |  | 		mod = module[0] | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-20 10:27:40 -06:00
										 |  |  | 	if mod == nil { | 
					
						
							|  |  |  | 		return Log() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-28 14:39:37 -06:00
										 |  |  | 	return ctx.cfg.Logging.Logger(mod) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 14:31:15 -05:00
										 |  |  | // Slogger returns a slog logger that is intended for use by | 
					
						
							|  |  |  | // the most recent module associated with the context. | 
					
						
							|  |  |  | func (ctx Context) Slogger() *slog.Logger { | 
					
						
							|  |  |  | 	if ctx.cfg == nil { | 
					
						
							|  |  |  | 		// often the case in tests; just use a dev logger | 
					
						
							|  |  |  | 		l, err := zap.NewDevelopment() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic("config missing, unable to create dev logger: " + err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-04-28 22:31:10 +08:00
										 |  |  | 		return slog.New(zapslog.NewHandler(l.Core())) | 
					
						
							| 
									
										
										
										
											2024-01-25 14:31:15 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	mod := ctx.Module() | 
					
						
							|  |  |  | 	if mod == nil { | 
					
						
							| 
									
										
										
										
											2025-04-28 22:31:10 +08:00
										 |  |  | 		return slog.New(zapslog.NewHandler(Log().Core())) | 
					
						
							| 
									
										
										
										
											2024-01-25 14:31:15 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-11-04 14:53:10 -07:00
										 |  |  | 	return slog.New(zapslog.NewHandler(ctx.cfg.Logging.Logger(mod).Core(), | 
					
						
							|  |  |  | 		zapslog.WithName(string(mod.CaddyModule().ID)), | 
					
						
							| 
									
										
										
										
											2024-01-25 14:31:15 -05:00
										 |  |  | 	)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 17:01:30 -04:00
										 |  |  | // Modules returns the lineage of modules that this context provisioned, | 
					
						
							|  |  |  | // with the most recent/current module being last in the list. | 
					
						
							|  |  |  | func (ctx Context) Modules() []Module { | 
					
						
							|  |  |  | 	mods := make([]Module, len(ctx.ancestry)) | 
					
						
							|  |  |  | 	copy(mods, ctx.ancestry) | 
					
						
							|  |  |  | 	return mods | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Module returns the current module, or the most recent one | 
					
						
							|  |  |  | // provisioned by the context. | 
					
						
							|  |  |  | func (ctx Context) Module() Module { | 
					
						
							|  |  |  | 	if len(ctx.ancestry) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ctx.ancestry[len(ctx.ancestry)-1] | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-04-17 14:19:14 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | // WithValue returns a new context with the given key-value pair. | 
					
						
							|  |  |  | func (ctx *Context) WithValue(key, value any) Context { | 
					
						
							|  |  |  | 	return Context{ | 
					
						
							|  |  |  | 		Context:         context.WithValue(ctx.Context, key, value), | 
					
						
							|  |  |  | 		moduleInstances: ctx.moduleInstances, | 
					
						
							|  |  |  | 		cfg:             ctx.cfg, | 
					
						
							|  |  |  | 		ancestry:        ctx.ancestry, | 
					
						
							|  |  |  | 		cleanupFuncs:    ctx.cleanupFuncs, | 
					
						
							|  |  |  | 		exitFuncs:       ctx.exitFuncs, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-03-29 08:15:43 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | // eventEmitter is a small interface that inverts dependencies for | 
					
						
							|  |  |  | // the caddyevents package, so the core can emit events without an | 
					
						
							|  |  |  | // import cycle (i.e. the caddy package doesn't have to import | 
					
						
							|  |  |  | // the caddyevents package, which imports the caddy package). | 
					
						
							|  |  |  | type eventEmitter interface { | 
					
						
							|  |  |  | 	Emit(ctx Context, eventName string, data map[string]any) Event | 
					
						
							|  |  |  | } |