mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			280 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package config
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"log"
 | 
						|
	"net"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/mholt/caddy/app"
 | 
						|
	"github.com/mholt/caddy/config/letsencrypt"
 | 
						|
	"github.com/mholt/caddy/config/parse"
 | 
						|
	"github.com/mholt/caddy/config/setup"
 | 
						|
	"github.com/mholt/caddy/middleware"
 | 
						|
	"github.com/mholt/caddy/server"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	DefaultHost = "0.0.0.0"
 | 
						|
	DefaultPort = "2015"
 | 
						|
	DefaultRoot = "."
 | 
						|
 | 
						|
	// DefaultConfigFile is the name of the configuration file that is loaded
 | 
						|
	// by default if no other file is specified.
 | 
						|
	DefaultConfigFile = "Caddyfile"
 | 
						|
)
 | 
						|
 | 
						|
// Load reads input (named filename) and parses it, returning server
 | 
						|
// configurations grouped by listening address.
 | 
						|
func Load(filename string, input io.Reader) (Group, error) {
 | 
						|
	var configs []server.Config
 | 
						|
 | 
						|
	// turn off timestamp for parsing
 | 
						|
	flags := log.Flags()
 | 
						|
	log.SetFlags(0)
 | 
						|
 | 
						|
	serverBlocks, err := parse.ServerBlocks(filename, input)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if len(serverBlocks) == 0 {
 | 
						|
		return Default()
 | 
						|
	}
 | 
						|
 | 
						|
	// Each server block represents similar hosts/addresses.
 | 
						|
	// Iterate each server block and make a config for each one,
 | 
						|
	// executing the directives that were parsed.
 | 
						|
	for i, sb := range serverBlocks {
 | 
						|
		onces := makeOnces()
 | 
						|
 | 
						|
		for _, addr := range sb.Addresses {
 | 
						|
			config := server.Config{
 | 
						|
				Host:       addr.Host,
 | 
						|
				Port:       addr.Port,
 | 
						|
				Root:       Root,
 | 
						|
				Middleware: make(map[string][]middleware.Middleware),
 | 
						|
				ConfigFile: filename,
 | 
						|
				AppName:    app.Name,
 | 
						|
				AppVersion: app.Version,
 | 
						|
			}
 | 
						|
 | 
						|
			// It is crucial that directives are executed in the proper order.
 | 
						|
			for _, dir := range directiveOrder {
 | 
						|
				// Execute directive if it is in the server block
 | 
						|
				if tokens, ok := sb.Tokens[dir.name]; ok {
 | 
						|
					// Each setup function gets a controller, which is the
 | 
						|
					// server config and the dispenser containing only
 | 
						|
					// this directive's tokens.
 | 
						|
					controller := &setup.Controller{
 | 
						|
						Config:    &config,
 | 
						|
						Dispenser: parse.NewDispenserTokens(filename, tokens),
 | 
						|
						OncePerServerBlock: func(f func() error) error {
 | 
						|
							var err error
 | 
						|
							onces[dir.name].Do(func() {
 | 
						|
								err = f()
 | 
						|
							})
 | 
						|
							return err
 | 
						|
						},
 | 
						|
						ServerBlockIndex: i,
 | 
						|
						ServerBlockHosts: sb.HostList(),
 | 
						|
					}
 | 
						|
 | 
						|
					midware, err := dir.setup(controller)
 | 
						|
					if err != nil {
 | 
						|
						return nil, err
 | 
						|
					}
 | 
						|
					if midware != nil {
 | 
						|
						// TODO: For now, we only support the default path scope /
 | 
						|
						config.Middleware["/"] = append(config.Middleware["/"], midware)
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if config.Port == "" {
 | 
						|
				config.Port = Port
 | 
						|
			}
 | 
						|
 | 
						|
			configs = append(configs, config)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// restore logging settings
 | 
						|
	log.SetFlags(flags)
 | 
						|
 | 
						|
	// secure all the things
 | 
						|
	configs, err = letsencrypt.Activate(configs)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// group by address/virtualhosts
 | 
						|
	return arrangeBindings(configs)
 | 
						|
}
 | 
						|
 | 
						|
// makeOnces makes a map of directive name to sync.Once
 | 
						|
// instance. This is intended to be called once per server
 | 
						|
// block when setting up configs so that Setup functions
 | 
						|
// for each directive can perform a task just once per
 | 
						|
// server block, even if there are multiple hosts on the block.
 | 
						|
//
 | 
						|
// We need one Once per directive, otherwise the first
 | 
						|
// directive to use it would exclude other directives from
 | 
						|
// using it at all, which would be a bug.
 | 
						|
func makeOnces() map[string]*sync.Once {
 | 
						|
	onces := make(map[string]*sync.Once)
 | 
						|
	for _, dir := range directiveOrder {
 | 
						|
		onces[dir.name] = new(sync.Once)
 | 
						|
	}
 | 
						|
	return onces
 | 
						|
}
 | 
						|
 | 
						|
// arrangeBindings groups configurations by their bind address. For example,
 | 
						|
// a server that should listen on localhost and another on 127.0.0.1 will
 | 
						|
// be grouped into the same address: 127.0.0.1. It will return an error
 | 
						|
// if an address is malformed or a TLS listener is configured on the
 | 
						|
// same address as a plaintext HTTP listener. The return value is a map of
 | 
						|
// bind address to list of configs that would become VirtualHosts on that
 | 
						|
// server. Use the keys of the returned map to create listeners, and use
 | 
						|
// the associated values to set up the virtualhosts.
 | 
						|
func arrangeBindings(allConfigs []server.Config) (map[*net.TCPAddr][]server.Config, error) {
 | 
						|
	addresses := make(map[*net.TCPAddr][]server.Config)
 | 
						|
 | 
						|
	// Group configs by bind address
 | 
						|
	for _, conf := range allConfigs {
 | 
						|
		newAddr, warnErr, fatalErr := resolveAddr(conf)
 | 
						|
		if fatalErr != nil {
 | 
						|
			return addresses, fatalErr
 | 
						|
		}
 | 
						|
		if warnErr != nil {
 | 
						|
			log.Println("[Warning]", warnErr)
 | 
						|
		}
 | 
						|
 | 
						|
		// Make sure to compare the string representation of the address,
 | 
						|
		// not the pointer, since a new *TCPAddr is created each time.
 | 
						|
		var existing bool
 | 
						|
		for addr := range addresses {
 | 
						|
			if addr.String() == newAddr.String() {
 | 
						|
				addresses[addr] = append(addresses[addr], conf)
 | 
						|
				existing = true
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if !existing {
 | 
						|
			addresses[newAddr] = append(addresses[newAddr], conf)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Don't allow HTTP and HTTPS to be served on the same address
 | 
						|
	for _, configs := range addresses {
 | 
						|
		isTLS := configs[0].TLS.Enabled
 | 
						|
		for _, config := range configs {
 | 
						|
			if config.TLS.Enabled != isTLS {
 | 
						|
				thisConfigProto, otherConfigProto := "HTTP", "HTTP"
 | 
						|
				if config.TLS.Enabled {
 | 
						|
					thisConfigProto = "HTTPS"
 | 
						|
				}
 | 
						|
				if configs[0].TLS.Enabled {
 | 
						|
					otherConfigProto = "HTTPS"
 | 
						|
				}
 | 
						|
				return addresses, fmt.Errorf("configuration error: Cannot multiplex %s (%s) and %s (%s) on same address",
 | 
						|
					configs[0].Address(), otherConfigProto, config.Address(), thisConfigProto)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return addresses, nil
 | 
						|
}
 | 
						|
 | 
						|
// resolveAddr determines the address (host and port) that a config will
 | 
						|
// bind to. The returned address, resolvAddr, should be used to bind the
 | 
						|
// listener or group the config with other configs using the same address.
 | 
						|
// The first error, if not nil, is just a warning and should be reported
 | 
						|
// but execution may continue. The second error, if not nil, is a real
 | 
						|
// problem and the server should not be started.
 | 
						|
//
 | 
						|
// This function handles edge cases gracefully. If a port name like
 | 
						|
// "http" or "https" is unknown to the system, this function will
 | 
						|
// change them to 80 or 443 respectively. If a hostname fails to
 | 
						|
// resolve, that host can still be served but will be listening on
 | 
						|
// the wildcard host instead. This function takes care of this for you.
 | 
						|
func resolveAddr(conf server.Config) (resolvAddr *net.TCPAddr, warnErr error, fatalErr error) {
 | 
						|
	bindHost := conf.BindHost
 | 
						|
 | 
						|
	resolvAddr, warnErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(bindHost, conf.Port))
 | 
						|
	if warnErr != nil {
 | 
						|
		// Most likely the host lookup failed or the port is unknown
 | 
						|
		tryPort := conf.Port
 | 
						|
 | 
						|
		switch errVal := warnErr.(type) {
 | 
						|
		case *net.AddrError:
 | 
						|
			if errVal.Err == "unknown port" {
 | 
						|
				// some odd Linux machines don't support these port names; see issue #136
 | 
						|
				switch conf.Port {
 | 
						|
				case "http":
 | 
						|
					tryPort = "80"
 | 
						|
				case "https":
 | 
						|
					tryPort = "443"
 | 
						|
				}
 | 
						|
			}
 | 
						|
			resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(bindHost, tryPort))
 | 
						|
			if fatalErr != nil {
 | 
						|
				return
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			// the hostname probably couldn't be resolved, just bind to wildcard then
 | 
						|
			resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort("0.0.0.0", tryPort))
 | 
						|
			if fatalErr != nil {
 | 
						|
				return
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// validDirective returns true if d is a valid
 | 
						|
// directive; false otherwise.
 | 
						|
func validDirective(d string) bool {
 | 
						|
	for _, dir := range directiveOrder {
 | 
						|
		if dir.name == d {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// NewDefault makes a default configuration, which
 | 
						|
// is empty except for root, host, and port,
 | 
						|
// which are essentials for serving the cwd.
 | 
						|
func NewDefault() server.Config {
 | 
						|
	return server.Config{
 | 
						|
		Root: Root,
 | 
						|
		Host: Host,
 | 
						|
		Port: Port,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Default obtains a default config and arranges
 | 
						|
// bindings so it's ready to use.
 | 
						|
func Default() (Group, error) {
 | 
						|
	return arrangeBindings([]server.Config{NewDefault()})
 | 
						|
}
 | 
						|
 | 
						|
// These defaults are configurable through the command line
 | 
						|
var (
 | 
						|
	// Site root
 | 
						|
	Root = DefaultRoot
 | 
						|
 | 
						|
	// Site host
 | 
						|
	Host = DefaultHost
 | 
						|
 | 
						|
	// Site port
 | 
						|
	Port = DefaultPort
 | 
						|
)
 | 
						|
 | 
						|
// Group maps network addresses to their configurations.
 | 
						|
type Group map[*net.TCPAddr][]server.Config
 |