From 859b5d7ea3b8f660ac68d9aea5a53d25a9a7422c Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Tue, 26 Mar 2019 12:00:54 -0600 Subject: Initial commit --- admin.go | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 admin.go (limited to 'admin.go') diff --git a/admin.go b/admin.go new file mode 100644 index 0000000..0672745 --- /dev/null +++ b/admin.go @@ -0,0 +1,130 @@ +package caddy2 + +import ( + "context" + "encoding/json" + "fmt" + "io" + "log" + "net" + "net/http" + "strings" + "sync" +) + +var ( + cfgEndptSrv *http.Server + cfgEndptSrvMu sync.Mutex +) + +// Start starts Caddy's administration endpoint. +func Start(addr string) error { + cfgEndptSrvMu.Lock() + defer cfgEndptSrvMu.Unlock() + + ln, err := net.Listen("tcp", addr) + if err != nil { + return err + } + + mux := http.NewServeMux() + mux.HandleFunc("/load", handleLoadConfig) + + for _, m := range GetModules("admin") { + moduleValue, err := m.New() + if err != nil { + return fmt.Errorf("initializing module '%s': %v", m.Name, err) + } + route := moduleValue.(AdminRoute) + mux.Handle(route.Pattern, route) + } + + cfgEndptSrv = &http.Server{ + Handler: mux, + } + + go cfgEndptSrv.Serve(ln) + + return nil +} + +// AdminRoute represents a route for the admin endpoint. +type AdminRoute struct { + http.Handler + Pattern string +} + +// Stop stops the API endpoint. +func Stop() error { + cfgEndptSrvMu.Lock() + defer cfgEndptSrvMu.Unlock() + + if cfgEndptSrv == nil { + return fmt.Errorf("no server") + } + + err := cfgEndptSrv.Shutdown(context.Background()) // TODO + if err != nil { + return fmt.Errorf("shutting down server: %v", err) + } + + cfgEndptSrv = nil + + return nil +} + +func handleLoadConfig(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + + if !strings.Contains(r.Header.Get("Content-Type"), "/json") { + http.Error(w, "unacceptable Content-Type", http.StatusBadRequest) + return + } + + err := Load(r.Body) + if err != nil { + log.Printf("[ADMIN][ERROR] loading config: %v", err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } +} + +// Load loads a configuration. +func Load(r io.Reader) error { + gc := globalConfig{modules: make(map[string]interface{})} + err := json.NewDecoder(r).Decode(&gc) + if err != nil { + return fmt.Errorf("decoding config: %v", err) + } + + for modName, rawMsg := range gc.Modules { + mod, ok := modules[modName] + if !ok { + return fmt.Errorf("unrecognized module: %s", modName) + } + + if mod.New != nil { + val, err := mod.New() + if err != nil { + return fmt.Errorf("initializing module '%s': %v", modName, err) + } + err = json.Unmarshal(rawMsg, &val) + if err != nil { + return fmt.Errorf("decoding module config: %s: %v", modName, err) + } + gc.modules[modName] = val + } + } + + return nil +} + +type globalConfig struct { + TestVal string `json:"testval"` + Modules map[string]json.RawMessage `json:"modules"` + TestArr []string `json:"test_arr"` + modules map[string]interface{} +} -- cgit v1.2.3