mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-23 22:27:38 -05:00
290 lines
9 KiB
Go
290 lines
9 KiB
Go
|
package caddy
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"sort"
|
||
|
|
||
|
"github.com/mholt/caddy/caddyfile"
|
||
|
)
|
||
|
|
||
|
// These are all the registered plugins.
|
||
|
var (
|
||
|
// serverTypes is a map of registered server types.
|
||
|
serverTypes = make(map[string]ServerType)
|
||
|
|
||
|
// plugins is a map of server type to map of plugin name to
|
||
|
// Plugin. These are the "general" plugins that may or may
|
||
|
// not be associated with a specific server type. If it's
|
||
|
// applicable to multiple server types or the server type is
|
||
|
// irrelevant, the key is empty string (""). But all plugins
|
||
|
// must have a name.
|
||
|
plugins = make(map[string]map[string]Plugin)
|
||
|
|
||
|
// parsingCallbacks maps server type to map of directive
|
||
|
// to list of callback functions. These aren't really
|
||
|
// plugins on their own, but are often registered from
|
||
|
// plugins.
|
||
|
parsingCallbacks = make(map[string]map[string][]func() error)
|
||
|
|
||
|
// caddyfileLoaders is the list of all Caddyfile loaders
|
||
|
// in registration order.
|
||
|
caddyfileLoaders []caddyfileLoader
|
||
|
)
|
||
|
|
||
|
// DescribePlugins returns a string describing the registered plugins.
|
||
|
func DescribePlugins() string {
|
||
|
str := "Server types:\n"
|
||
|
for name := range serverTypes {
|
||
|
str += " " + name + "\n"
|
||
|
}
|
||
|
|
||
|
// List the loaders in registration order
|
||
|
str += "\nCaddyfile loaders:\n"
|
||
|
for _, loader := range caddyfileLoaders {
|
||
|
str += " " + loader.name + "\n"
|
||
|
}
|
||
|
if defaultCaddyfileLoader.name != "" {
|
||
|
str += " " + defaultCaddyfileLoader.name + "\n"
|
||
|
}
|
||
|
|
||
|
// Let's alphabetize the rest of these...
|
||
|
var others []string
|
||
|
for stype, stypePlugins := range plugins {
|
||
|
for name := range stypePlugins {
|
||
|
var s string
|
||
|
if stype != "" {
|
||
|
s = stype + "."
|
||
|
}
|
||
|
s += name
|
||
|
others = append(others, s)
|
||
|
}
|
||
|
}
|
||
|
sort.Strings(others)
|
||
|
str += "\nOther plugins:\n"
|
||
|
for _, name := range others {
|
||
|
str += " " + name + "\n"
|
||
|
}
|
||
|
|
||
|
return str
|
||
|
}
|
||
|
|
||
|
// ValidDirectives returns the list of all directives that are
|
||
|
// recognized for the server type serverType. However, not all
|
||
|
// directives may be installed. This makes it possible to give
|
||
|
// more helpful error messages, like "did you mean ..." or
|
||
|
// "maybe you need to plug in ...".
|
||
|
func ValidDirectives(serverType string) []string {
|
||
|
stype, err := getServerType(serverType)
|
||
|
if err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
return stype.Directives
|
||
|
}
|
||
|
|
||
|
// serverListener pairs a server to its listener.
|
||
|
type serverListener struct {
|
||
|
server Server
|
||
|
listener net.Listener
|
||
|
}
|
||
|
|
||
|
// Context is a type that carries a server type through
|
||
|
// the load and setup phase; it maintains the state
|
||
|
// between loading the Caddyfile, then executing its
|
||
|
// directives, then making the servers for Caddy to
|
||
|
// manage. Typically, such state involves configuration
|
||
|
// structs, etc.
|
||
|
type Context interface {
|
||
|
InspectServerBlocks(string, []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error)
|
||
|
MakeServers() ([]Server, error)
|
||
|
}
|
||
|
|
||
|
// RegisterServerType registers a server type srv by its
|
||
|
// name, typeName.
|
||
|
func RegisterServerType(typeName string, srv ServerType) {
|
||
|
if _, ok := serverTypes[typeName]; ok {
|
||
|
panic("server type already registered")
|
||
|
}
|
||
|
serverTypes[typeName] = srv
|
||
|
}
|
||
|
|
||
|
// ServerType contains information about a server type.
|
||
|
type ServerType struct {
|
||
|
// List of directives, in execution order, that are
|
||
|
// valid for this server type. Directives should be
|
||
|
// one word if possible and lower-cased.
|
||
|
Directives []string
|
||
|
|
||
|
// InspectServerBlocks is an optional callback that is
|
||
|
// executed after loading the tokens for each server
|
||
|
// block but before executing the directives in them.
|
||
|
// This func may modify the server blocks and return
|
||
|
// new ones to be used.
|
||
|
InspectServerBlocks func(sourceFile string, serverBlocks []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error)
|
||
|
|
||
|
// MakeServers is a callback that makes the server
|
||
|
// instances.
|
||
|
MakeServers func() ([]Server, error)
|
||
|
|
||
|
// DefaultInput returns a default config input if none
|
||
|
// is otherwise loaded.
|
||
|
DefaultInput func() Input
|
||
|
|
||
|
NewContext func() Context
|
||
|
}
|
||
|
|
||
|
// Plugin is a type which holds information about a plugin.
|
||
|
type Plugin struct {
|
||
|
// The plugin must have a name: lower case and one word.
|
||
|
// If this plugin has an action, it must be the name of
|
||
|
// the directive to attach to. A name is always required.
|
||
|
Name string
|
||
|
|
||
|
// ServerType is the type of server this plugin is for.
|
||
|
// Can be empty if not applicable, or if the plugin
|
||
|
// can associate with any server type.
|
||
|
ServerType string
|
||
|
|
||
|
// Action is the plugin's setup function, if associated
|
||
|
// with a directive in the Caddyfile.
|
||
|
Action SetupFunc
|
||
|
}
|
||
|
|
||
|
// RegisterPlugin plugs in plugin. All plugins should register
|
||
|
// themselves, even if they do not perform an action associated
|
||
|
// with a directive. It is important for the process to know
|
||
|
// which plugins are available.
|
||
|
func RegisterPlugin(plugin Plugin) {
|
||
|
if plugin.Name == "" {
|
||
|
panic("plugin must have a name")
|
||
|
}
|
||
|
if _, ok := plugins[plugin.ServerType]; !ok {
|
||
|
plugins[plugin.ServerType] = make(map[string]Plugin)
|
||
|
}
|
||
|
if _, dup := plugins[plugin.ServerType][plugin.Name]; dup {
|
||
|
panic("plugin named " + plugin.Name + " already registered for server type " + plugin.ServerType)
|
||
|
}
|
||
|
plugins[plugin.ServerType][plugin.Name] = plugin
|
||
|
}
|
||
|
|
||
|
// RegisterParsingCallback registers callback to be called after
|
||
|
// executing the directive afterDir for server type serverType.
|
||
|
func RegisterParsingCallback(serverType, afterDir string, callback func() error) {
|
||
|
if _, ok := parsingCallbacks[serverType]; !ok {
|
||
|
parsingCallbacks[serverType] = make(map[string][]func() error)
|
||
|
}
|
||
|
parsingCallbacks[serverType][afterDir] = append(parsingCallbacks[serverType][afterDir], callback)
|
||
|
}
|
||
|
|
||
|
// SetupFunc is used to set up a plugin, or in other words,
|
||
|
// execute a directive. It will be called once per key for
|
||
|
// each server block it appears in.
|
||
|
type SetupFunc func(c *Controller) error
|
||
|
|
||
|
// DirectiveAction gets the action for directive dir of
|
||
|
// server type serverType.
|
||
|
func DirectiveAction(serverType, dir string) (SetupFunc, error) {
|
||
|
if stypePlugins, ok := plugins[serverType]; ok {
|
||
|
if plugin, ok := stypePlugins[dir]; ok {
|
||
|
return plugin.Action, nil
|
||
|
}
|
||
|
}
|
||
|
if genericPlugins, ok := plugins[""]; ok {
|
||
|
if plugin, ok := genericPlugins[dir]; ok {
|
||
|
return plugin.Action, nil
|
||
|
}
|
||
|
}
|
||
|
return nil, fmt.Errorf("no action found for directive '%s' with server type '%s' (missing a plugin?)",
|
||
|
dir, serverType)
|
||
|
}
|
||
|
|
||
|
// Loader is a type that can load a Caddyfile.
|
||
|
// It is passed the name of the server type.
|
||
|
// It returns an error only if something went
|
||
|
// wrong, not simply if there is no Caddyfile
|
||
|
// for this loader to load.
|
||
|
//
|
||
|
// A Loader should only load the Caddyfile if
|
||
|
// a certain condition or requirement is met,
|
||
|
// as returning a non-nil Input value along with
|
||
|
// another Loader will result in an error.
|
||
|
// In other words, loading the Caddyfile must
|
||
|
// be deliberate & deterministic, not haphazard.
|
||
|
//
|
||
|
// The exception is the default Caddyfile loader,
|
||
|
// which will be called only if no other Caddyfile
|
||
|
// loaders return a non-nil Input. The default
|
||
|
// loader may always return an Input value.
|
||
|
type Loader interface {
|
||
|
Load(string) (Input, error)
|
||
|
}
|
||
|
|
||
|
// LoaderFunc is a convenience type similar to http.HandlerFunc
|
||
|
// that allows you to use a plain function as a Load() method.
|
||
|
type LoaderFunc func(string) (Input, error)
|
||
|
|
||
|
// Load loads a Caddyfile.
|
||
|
func (lf LoaderFunc) Load(serverType string) (Input, error) {
|
||
|
return lf(serverType)
|
||
|
}
|
||
|
|
||
|
// RegisterCaddyfileLoader registers loader named name.
|
||
|
func RegisterCaddyfileLoader(name string, loader Loader) {
|
||
|
caddyfileLoaders = append(caddyfileLoaders, caddyfileLoader{name: name, loader: loader})
|
||
|
}
|
||
|
|
||
|
// SetDefaultCaddyfileLoader registers loader by name
|
||
|
// as the default Caddyfile loader if no others produce
|
||
|
// a Caddyfile. If another Caddyfile loader has already
|
||
|
// been set as the default, this replaces it.
|
||
|
//
|
||
|
// Do not call RegisterCaddyfileLoader on the same
|
||
|
// loader; that would be redundant.
|
||
|
func SetDefaultCaddyfileLoader(name string, loader Loader) {
|
||
|
defaultCaddyfileLoader = caddyfileLoader{name: name, loader: loader}
|
||
|
}
|
||
|
|
||
|
// loadCaddyfileInput iterates the registered Caddyfile loaders
|
||
|
// and, if needed, calls the default loader, to load a Caddyfile.
|
||
|
// It is an error if any of the loaders return an error or if
|
||
|
// more than one loader returns a Caddyfile.
|
||
|
func loadCaddyfileInput(serverType string) (Input, error) {
|
||
|
var loadedBy string
|
||
|
var caddyfileToUse Input
|
||
|
for _, l := range caddyfileLoaders {
|
||
|
if cdyfile, err := l.loader.Load(serverType); cdyfile != nil {
|
||
|
if caddyfileToUse != nil {
|
||
|
return nil, fmt.Errorf("Caddyfile loaded multiple times; first by %s, then by %s", loadedBy, l.name)
|
||
|
}
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
loaderUsed = l
|
||
|
caddyfileToUse = cdyfile
|
||
|
loadedBy = l.name
|
||
|
}
|
||
|
}
|
||
|
if caddyfileToUse == nil && defaultCaddyfileLoader.loader != nil {
|
||
|
cdyfile, err := defaultCaddyfileLoader.loader.Load(serverType)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if cdyfile != nil {
|
||
|
loaderUsed = defaultCaddyfileLoader
|
||
|
caddyfileToUse = cdyfile
|
||
|
}
|
||
|
}
|
||
|
return caddyfileToUse, nil
|
||
|
}
|
||
|
|
||
|
// caddyfileLoader pairs the name of a loader to the loader.
|
||
|
type caddyfileLoader struct {
|
||
|
name string
|
||
|
loader Loader
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
defaultCaddyfileLoader caddyfileLoader // the default loader if all else fail
|
||
|
loaderUsed caddyfileLoader // the loader that was used (relevant for reloads)
|
||
|
)
|