From f783290f40febd3eef2a299911ad95bab4d2b414 Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Mon, 1 Aug 2022 13:36:22 -0600 Subject: caddyhttp: Implement `caddy respond` command (#4870) --- caddy.go | 81 +++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 36 deletions(-) (limited to 'caddy.go') diff --git a/caddy.go b/caddy.go index 98ee5b3..20da5de 100644 --- a/caddy.go +++ b/caddy.go @@ -141,8 +141,8 @@ func changeConfig(method, path string, input []byte, ifMatchHeader string, force return fmt.Errorf("method not allowed") } - currentCfgMu.Lock() - defer currentCfgMu.Unlock() + currentCtxMu.Lock() + defer currentCtxMu.Unlock() if ifMatchHeader != "" { // expect the first and last character to be quotes @@ -242,15 +242,15 @@ func changeConfig(method, path string, input []byte, ifMatchHeader string, force // readConfig traverses the current config to path // and writes its JSON encoding to out. func readConfig(path string, out io.Writer) error { - currentCfgMu.RLock() - defer currentCfgMu.RUnlock() + currentCtxMu.RLock() + defer currentCtxMu.RUnlock() return unsyncedConfigAccess(http.MethodGet, path, nil, out) } // indexConfigObjects recursively searches ptr for object fields named // "@id" and maps that ID value to the full configPath in the index. // This function is NOT safe for concurrent access; obtain a write lock -// on currentCfgMu. +// on currentCtxMu. func indexConfigObjects(ptr interface{}, configPath string, index map[string]string) error { switch val := ptr.(type) { case map[string]interface{}: @@ -290,7 +290,7 @@ func indexConfigObjects(ptr interface{}, configPath string, index map[string]str // it as the new config, replacing any other current config. // It does NOT update the raw config state, as this is a // lower-level function; most callers will want to use Load -// instead. A write lock on currentCfgMu is required! If +// instead. A write lock on currentCtxMu is required! If // allowPersist is false, it will not be persisted to disk, // even if it is configured to. func unsyncedDecodeAndRun(cfgJSON []byte, allowPersist bool) error { @@ -319,17 +319,17 @@ func unsyncedDecodeAndRun(cfgJSON []byte, allowPersist bool) error { } // run the new config and start all its apps - err = run(newCfg, true) + ctx, err := run(newCfg, true) if err != nil { return err } - // swap old config with the new one - oldCfg := currentCfg - currentCfg = newCfg + // swap old context (including its config) with the new one + oldCtx := currentCtx + currentCtx = ctx // Stop, Cleanup each old app - unsyncedStop(oldCfg) + unsyncedStop(oldCtx) // autosave a non-nil config, if not disabled if allowPersist && @@ -373,7 +373,7 @@ func unsyncedDecodeAndRun(cfgJSON []byte, allowPersist bool) error { // This is a low-level function; most callers // will want to use Run instead, which also // updates the config's raw state. -func run(newCfg *Config, start bool) error { +func run(newCfg *Config, start bool) (Context, error) { // because we will need to roll back any state // modifications if this function errors, we // keep a single error value and scope all @@ -404,8 +404,8 @@ func run(newCfg *Config, start bool) error { cancel() // also undo any other state changes we made - if currentCfg != nil { - certmagic.Default.Storage = currentCfg.storage + if currentCtx.cfg != nil { + certmagic.Default.Storage = currentCtx.cfg.storage } } }() @@ -417,14 +417,14 @@ func run(newCfg *Config, start bool) error { } err = newCfg.Logging.openLogs(ctx) if err != nil { - return err + return ctx, err } // start the admin endpoint (and stop any prior one) if start { err = replaceLocalAdminServer(newCfg) if err != nil { - return fmt.Errorf("starting caddy administration endpoint: %v", err) + return ctx, fmt.Errorf("starting caddy administration endpoint: %v", err) } } @@ -453,7 +453,7 @@ func run(newCfg *Config, start bool) error { return nil }() if err != nil { - return err + return ctx, err } // Load and Provision each app and their submodules @@ -466,18 +466,18 @@ func run(newCfg *Config, start bool) error { return nil }() if err != nil { - return err + return ctx, err } if !start { - return nil + return ctx, nil } // Provision any admin routers which may need to access // some of the other apps at runtime err = newCfg.Admin.provisionAdminRouters(ctx) if err != nil { - return err + return ctx, err } // Start @@ -502,12 +502,12 @@ func run(newCfg *Config, start bool) error { return nil }() if err != nil { - return err + return ctx, err } // now that the user's config is running, finish setting up anything else, // such as remote admin endpoint, config loader, etc. - return finishSettingUp(ctx, newCfg) + return ctx, finishSettingUp(ctx, newCfg) } // finishSettingUp should be run after all apps have successfully started. @@ -612,10 +612,10 @@ type ConfigLoader interface { // stop the others. Stop should only be called // if not replacing with a new config. func Stop() error { - currentCfgMu.Lock() - defer currentCfgMu.Unlock() - unsyncedStop(currentCfg) - currentCfg = nil + currentCtxMu.Lock() + defer currentCtxMu.Unlock() + unsyncedStop(currentCtx) + currentCtx = Context{} rawCfgJSON = nil rawCfgIndex = nil rawCfg[rawConfigKey] = nil @@ -628,13 +628,13 @@ func Stop() error { // it is logged and the function continues stopping // the next app. This function assumes all apps in // cfg were successfully started first. -func unsyncedStop(cfg *Config) { - if cfg == nil { +func unsyncedStop(ctx Context) { + if ctx.cfg == nil { return } // stop each app - for name, a := range cfg.apps { + for name, a := range ctx.cfg.apps { err := a.Stop() if err != nil { log.Printf("[ERROR] stop %s: %v", name, err) @@ -642,13 +642,13 @@ func unsyncedStop(cfg *Config) { } // clean up all modules - cfg.cancelFunc() + ctx.cfg.cancelFunc() } // Validate loads, provisions, and validates // cfg, but does not start running it. func Validate(cfg *Config) error { - err := run(cfg, false) + _, err := run(cfg, false) if err == nil { cfg.cancelFunc() // call Cleanup on all modules } @@ -823,16 +823,25 @@ func goModule(mod *debug.Module) *debug.Module { return mod } +func ActiveContext() Context { + currentCtxMu.RLock() + defer currentCtxMu.RUnlock() + return currentCtx +} + // CtxKey is a value type for use with context.WithValue. type CtxKey string // This group of variables pertains to the current configuration. var ( - // currentCfgMu protects everything in this var block. - currentCfgMu sync.RWMutex - - // currentCfg is the currently-running configuration. - currentCfg *Config + // currentCtxMu protects everything in this var block. + currentCtxMu sync.RWMutex + + // currentCtx is the root context for the currently-running + // configuration, which can be accessed through this value. + // If the Config contained in this value is not nil, then + // a config is currently active/running. + currentCtx Context // rawCfg is the current, generic-decoded configuration; // we initialize it as a map with one field ("config") -- cgit v1.2.3