mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-30 18:22:49 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			329 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			10 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][]ParsingCallback)
 | |
| 
 | |
| 	// 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 and/or packetconn.
 | |
| type ServerListener struct {
 | |
| 	server   Server
 | |
| 	listener net.Listener
 | |
| 	packet   net.PacketConn
 | |
| }
 | |
| 
 | |
| // LocalAddr returns the local network address of the packetconn. It returns
 | |
| // nil when it is not set.
 | |
| func (s ServerListener) LocalAddr() net.Addr {
 | |
| 	if s.packet == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return s.packet.LocalAddr()
 | |
| }
 | |
| 
 | |
| // Addr returns the listener's network address. It returns nil when it is
 | |
| // not set.
 | |
| func (s ServerListener) Addr() net.Addr {
 | |
| 	if s.listener == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return s.listener.Addr()
 | |
| }
 | |
| 
 | |
| // Context is a type which 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 {
 | |
| 	// Called after the Caddyfile is parsed into server
 | |
| 	// blocks but before the directives are executed,
 | |
| 	// this method gives you an opportunity to inspect
 | |
| 	// the server blocks and prepare for the execution
 | |
| 	// of directives. Return the server blocks (which
 | |
| 	// you may modify, if desired) and an error, if any.
 | |
| 	// The first argument is the name or path to the
 | |
| 	// configuration file (Caddyfile).
 | |
| 	//
 | |
| 	// This function can be a no-op and simply return its
 | |
| 	// input if there is nothing to do here.
 | |
| 	InspectServerBlocks(string, []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error)
 | |
| 
 | |
| 	// This is what Caddy calls to make server instances.
 | |
| 	// By this time, all directives have been executed and,
 | |
| 	// presumably, the context has enough state to produce
 | |
| 	// server instances for Caddy to start.
 | |
| 	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 {
 | |
| 	// Function that returns the list of directives, in
 | |
| 	// execution order, that are valid for this server
 | |
| 	// type. Directives should be one word if possible
 | |
| 	// and lower-cased.
 | |
| 	Directives func() []string
 | |
| 
 | |
| 	// DefaultInput returns a default config input if none
 | |
| 	// is otherwise loaded. This is optional, but highly
 | |
| 	// recommended, otherwise a blank Caddyfile will be
 | |
| 	// used.
 | |
| 	DefaultInput func() Input
 | |
| 
 | |
| 	// The function that produces a new server type context.
 | |
| 	// This will be called when a new Caddyfile is being
 | |
| 	// loaded, parsed, and executed independently of any
 | |
| 	// startup phases before this one. It's a way to keep
 | |
| 	// each set of server instances separate and to reduce
 | |
| 	// the amount of global state you need.
 | |
| 	NewContext func() Context
 | |
| }
 | |
| 
 | |
| // Plugin is a type which holds information about a plugin.
 | |
| type Plugin struct {
 | |
| 	// 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.
 | |
| //
 | |
| // 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 that invokes it. A name is always required
 | |
| // and must be unique for the server type.
 | |
| func RegisterPlugin(name string, plugin Plugin) {
 | |
| 	if 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][name]; dup {
 | |
| 		panic("plugin named " + name + " already registered for server type " + plugin.ServerType)
 | |
| 	}
 | |
| 	plugins[plugin.ServerType][name] = plugin
 | |
| }
 | |
| 
 | |
| // ParsingCallback is a function that is called after
 | |
| // a directive's setup functions have been executed
 | |
| // for all the server blocks.
 | |
| type ParsingCallback func(Context) error
 | |
| 
 | |
| // RegisterParsingCallback registers callback to be called after
 | |
| // executing the directive afterDir for server type serverType.
 | |
| func RegisterParsingCallback(serverType, afterDir string, callback ParsingCallback) {
 | |
| 	if _, ok := parsingCallbacks[serverType]; !ok {
 | |
| 		parsingCallbacks[serverType] = make(map[string][]ParsingCallback)
 | |
| 	}
 | |
| 	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(serverType 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(serverType 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 {
 | |
| 		cdyfile, err := l.loader.Load(serverType)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("loading Caddyfile via %s: %v", l.name, err)
 | |
| 		}
 | |
| 		if cdyfile != nil {
 | |
| 			if caddyfileToUse != nil {
 | |
| 				return nil, fmt.Errorf("Caddyfile loaded multiple times; first by %s, then by %s", loadedBy, l.name)
 | |
| 			}
 | |
| 			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)
 | |
| )
 |