mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	httpcaddyfile: Make global options pluggable (#3265)
* httpcaddyfile: Make global options pluggable * httpcaddyfile: Add a global options adapt test * httpcaddyfile: Wrap err Co-Authored-By: Dave Henderson <dhenderson@gmail.com> * httpcaddyfile: Revert wrap err Co-authored-by: Dave Henderson <dhenderson@gmail.com>
This commit is contained in:
		
							parent
							
								
									4c55d26f11
								
							
						
					
					
						commit
						dc9f4f13fc
					
				@ -120,6 +120,17 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RegisterGlobalOption registers a unique global option opt with
 | 
				
			||||||
 | 
					// an associated unmarshaling (setup) function. When the global
 | 
				
			||||||
 | 
					// option opt is encountered in a Caddyfile, setupFunc will be
 | 
				
			||||||
 | 
					// called to unmarshal its tokens.
 | 
				
			||||||
 | 
					func RegisterGlobalOption(opt string, setupFunc UnmarshalGlobalFunc) {
 | 
				
			||||||
 | 
						if _, ok := registeredGlobalOptions[opt]; ok {
 | 
				
			||||||
 | 
							panic("global option " + opt + " already registered")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						registeredGlobalOptions[opt] = setupFunc
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Helper is a type which helps setup a value from
 | 
					// Helper is a type which helps setup a value from
 | 
				
			||||||
// Caddyfile tokens.
 | 
					// Caddyfile tokens.
 | 
				
			||||||
type Helper struct {
 | 
					type Helper struct {
 | 
				
			||||||
@ -454,6 +465,13 @@ type (
 | 
				
			|||||||
	// for you. These are passed to a call to
 | 
						// for you. These are passed to a call to
 | 
				
			||||||
	// RegisterHandlerDirective.
 | 
						// RegisterHandlerDirective.
 | 
				
			||||||
	UnmarshalHandlerFunc func(h Helper) (caddyhttp.MiddlewareHandler, error)
 | 
						UnmarshalHandlerFunc func(h Helper) (caddyhttp.MiddlewareHandler, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// UnmarshalGlobalFunc is a function which can unmarshal Caddyfile
 | 
				
			||||||
 | 
						// tokens into a global option config value using a Helper type.
 | 
				
			||||||
 | 
						// These are passed in a call to RegisterGlobalOption.
 | 
				
			||||||
 | 
						UnmarshalGlobalFunc func(d *caddyfile.Dispenser) (interface{}, error)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var registeredDirectives = make(map[string]UnmarshalFunc)
 | 
					var registeredDirectives = make(map[string]UnmarshalFunc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var registeredGlobalOptions = make(map[string]UnmarshalGlobalFunc)
 | 
				
			||||||
 | 
				
			|||||||
@ -284,39 +284,18 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
 | 
				
			|||||||
		var val interface{}
 | 
							var val interface{}
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		disp := caddyfile.NewDispenser(segment)
 | 
							disp := caddyfile.NewDispenser(segment)
 | 
				
			||||||
		switch dir {
 | 
					
 | 
				
			||||||
		case "debug":
 | 
							dirFunc, ok := registeredGlobalOptions[dir]
 | 
				
			||||||
			val = true
 | 
							if !ok {
 | 
				
			||||||
		case "http_port":
 | 
								tkn := segment[0]
 | 
				
			||||||
			val, err = parseOptHTTPPort(disp)
 | 
								return nil, fmt.Errorf("%s:%d: unrecognized global option: %s", tkn.File, tkn.Line, dir)
 | 
				
			||||||
		case "https_port":
 | 
					 | 
				
			||||||
			val, err = parseOptHTTPSPort(disp)
 | 
					 | 
				
			||||||
		case "default_sni":
 | 
					 | 
				
			||||||
			val, err = parseOptSingleString(disp)
 | 
					 | 
				
			||||||
		case "order":
 | 
					 | 
				
			||||||
			val, err = parseOptOrder(disp)
 | 
					 | 
				
			||||||
		case "experimental_http3":
 | 
					 | 
				
			||||||
			val, err = parseOptExperimentalHTTP3(disp)
 | 
					 | 
				
			||||||
		case "storage":
 | 
					 | 
				
			||||||
			val, err = parseOptStorage(disp)
 | 
					 | 
				
			||||||
		case "acme_ca", "acme_dns", "acme_ca_root":
 | 
					 | 
				
			||||||
			val, err = parseOptSingleString(disp)
 | 
					 | 
				
			||||||
		case "email":
 | 
					 | 
				
			||||||
			val, err = parseOptSingleString(disp)
 | 
					 | 
				
			||||||
		case "admin":
 | 
					 | 
				
			||||||
			val, err = parseOptAdmin(disp)
 | 
					 | 
				
			||||||
		case "on_demand_tls":
 | 
					 | 
				
			||||||
			val, err = parseOptOnDemand(disp)
 | 
					 | 
				
			||||||
		case "local_certs":
 | 
					 | 
				
			||||||
			val = true
 | 
					 | 
				
			||||||
		case "key_type":
 | 
					 | 
				
			||||||
			val, err = parseOptSingleString(disp)
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("unrecognized parameter name: %s", dir)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							val, err = dirFunc(disp)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("%s: %v", dir, err)
 | 
								return nil, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		options[dir] = val
 | 
							options[dir] = val
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -23,7 +23,29 @@ import (
 | 
				
			|||||||
	"github.com/caddyserver/caddy/v2/modules/caddytls"
 | 
						"github.com/caddyserver/caddy/v2/modules/caddytls"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseOptHTTPPort(d *caddyfile.Dispenser) (int, error) {
 | 
					func init() {
 | 
				
			||||||
 | 
						RegisterGlobalOption("debug", parseOptTrue)
 | 
				
			||||||
 | 
						RegisterGlobalOption("http_port", parseOptHTTPPort)
 | 
				
			||||||
 | 
						RegisterGlobalOption("https_port", parseOptHTTPSPort)
 | 
				
			||||||
 | 
						RegisterGlobalOption("default_sni", parseOptSingleString)
 | 
				
			||||||
 | 
						RegisterGlobalOption("order", parseOptOrder)
 | 
				
			||||||
 | 
						RegisterGlobalOption("experimental_http3", parseOptTrue)
 | 
				
			||||||
 | 
						RegisterGlobalOption("storage", parseOptStorage)
 | 
				
			||||||
 | 
						RegisterGlobalOption("acme_ca", parseOptSingleString)
 | 
				
			||||||
 | 
						RegisterGlobalOption("acme_dns", parseOptSingleString)
 | 
				
			||||||
 | 
						RegisterGlobalOption("acme_ca_root", parseOptSingleString)
 | 
				
			||||||
 | 
						RegisterGlobalOption("email", parseOptSingleString)
 | 
				
			||||||
 | 
						RegisterGlobalOption("admin", parseOptAdmin)
 | 
				
			||||||
 | 
						RegisterGlobalOption("on_demand_tls", parseOptOnDemand)
 | 
				
			||||||
 | 
						RegisterGlobalOption("local_certs", parseOptTrue)
 | 
				
			||||||
 | 
						RegisterGlobalOption("key_type", parseOptSingleString)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseOptTrue(d *caddyfile.Dispenser) (interface{}, error) {
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseOptHTTPPort(d *caddyfile.Dispenser) (interface{}, error) {
 | 
				
			||||||
	var httpPort int
 | 
						var httpPort int
 | 
				
			||||||
	for d.Next() {
 | 
						for d.Next() {
 | 
				
			||||||
		var httpPortStr string
 | 
							var httpPortStr string
 | 
				
			||||||
@ -39,7 +61,7 @@ func parseOptHTTPPort(d *caddyfile.Dispenser) (int, error) {
 | 
				
			|||||||
	return httpPort, nil
 | 
						return httpPort, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseOptHTTPSPort(d *caddyfile.Dispenser) (int, error) {
 | 
					func parseOptHTTPSPort(d *caddyfile.Dispenser) (interface{}, error) {
 | 
				
			||||||
	var httpsPort int
 | 
						var httpsPort int
 | 
				
			||||||
	for d.Next() {
 | 
						for d.Next() {
 | 
				
			||||||
		var httpsPortStr string
 | 
							var httpsPortStr string
 | 
				
			||||||
@ -55,11 +77,7 @@ func parseOptHTTPSPort(d *caddyfile.Dispenser) (int, error) {
 | 
				
			|||||||
	return httpsPort, nil
 | 
						return httpsPort, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseOptExperimentalHTTP3(d *caddyfile.Dispenser) (bool, error) {
 | 
					func parseOptOrder(d *caddyfile.Dispenser) (interface{}, error) {
 | 
				
			||||||
	return true, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func parseOptOrder(d *caddyfile.Dispenser) ([]string, error) {
 | 
					 | 
				
			||||||
	newOrder := directiveOrder
 | 
						newOrder := directiveOrder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for d.Next() {
 | 
						for d.Next() {
 | 
				
			||||||
@ -135,7 +153,7 @@ func parseOptOrder(d *caddyfile.Dispenser) ([]string, error) {
 | 
				
			|||||||
	return newOrder, nil
 | 
						return newOrder, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseOptStorage(d *caddyfile.Dispenser) (caddy.StorageConverter, error) {
 | 
					func parseOptStorage(d *caddyfile.Dispenser) (interface{}, error) {
 | 
				
			||||||
	if !d.Next() { // consume option name
 | 
						if !d.Next() { // consume option name
 | 
				
			||||||
		return nil, d.ArgErr()
 | 
							return nil, d.ArgErr()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -162,7 +180,7 @@ func parseOptStorage(d *caddyfile.Dispenser) (caddy.StorageConverter, error) {
 | 
				
			|||||||
	return storage, nil
 | 
						return storage, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseOptSingleString(d *caddyfile.Dispenser) (string, error) {
 | 
					func parseOptSingleString(d *caddyfile.Dispenser) (interface{}, error) {
 | 
				
			||||||
	d.Next() // consume parameter name
 | 
						d.Next() // consume parameter name
 | 
				
			||||||
	if !d.Next() {
 | 
						if !d.Next() {
 | 
				
			||||||
		return "", d.ArgErr()
 | 
							return "", d.ArgErr()
 | 
				
			||||||
@ -174,7 +192,7 @@ func parseOptSingleString(d *caddyfile.Dispenser) (string, error) {
 | 
				
			|||||||
	return val, nil
 | 
						return val, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseOptAdmin(d *caddyfile.Dispenser) (string, error) {
 | 
					func parseOptAdmin(d *caddyfile.Dispenser) (interface{}, error) {
 | 
				
			||||||
	if d.Next() {
 | 
						if d.Next() {
 | 
				
			||||||
		var listenAddress string
 | 
							var listenAddress string
 | 
				
			||||||
		if !d.AllArgs(&listenAddress) {
 | 
							if !d.AllArgs(&listenAddress) {
 | 
				
			||||||
@ -188,7 +206,7 @@ func parseOptAdmin(d *caddyfile.Dispenser) (string, error) {
 | 
				
			|||||||
	return "", nil
 | 
						return "", nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseOptOnDemand(d *caddyfile.Dispenser) (*caddytls.OnDemandConfig, error) {
 | 
					func parseOptOnDemand(d *caddyfile.Dispenser) (interface{}, error) {
 | 
				
			||||||
	var ond *caddytls.OnDemandConfig
 | 
						var ond *caddytls.OnDemandConfig
 | 
				
			||||||
	for d.Next() {
 | 
						for d.Next() {
 | 
				
			||||||
		if d.NextArg() {
 | 
							if d.NextArg() {
 | 
				
			||||||
 | 
				
			|||||||
@ -415,3 +415,77 @@ func TestNotBlockMerging(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}`)
 | 
					}`)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGlobalOptions(t *testing.T) {
 | 
				
			||||||
 | 
						caddytest.AssertAdapt(t, ` 
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							debug
 | 
				
			||||||
 | 
							http_port 8080
 | 
				
			||||||
 | 
							https_port 8443
 | 
				
			||||||
 | 
							default_sni localhost
 | 
				
			||||||
 | 
							order root first
 | 
				
			||||||
 | 
							storage file_system {
 | 
				
			||||||
 | 
								root /data
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							acme_ca https://example.com
 | 
				
			||||||
 | 
							acme_ca_root /path/to/ca.crt
 | 
				
			||||||
 | 
							email test@example.com
 | 
				
			||||||
 | 
							admin off
 | 
				
			||||||
 | 
							on_demand_tls {
 | 
				
			||||||
 | 
								ask https://example.com
 | 
				
			||||||
 | 
								interval 30s
 | 
				
			||||||
 | 
								burst 20
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							local_certs
 | 
				
			||||||
 | 
							key_type ed25519
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						:80
 | 
				
			||||||
 | 
					  `, "caddyfile", `{
 | 
				
			||||||
 | 
						"admin": {
 | 
				
			||||||
 | 
							"disabled": true
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"logging": {
 | 
				
			||||||
 | 
							"logs": {
 | 
				
			||||||
 | 
								"default": {
 | 
				
			||||||
 | 
									"level": "DEBUG"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"storage": {
 | 
				
			||||||
 | 
							"module": "file_system",
 | 
				
			||||||
 | 
							"root": "/data"
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"apps": {
 | 
				
			||||||
 | 
							"http": {
 | 
				
			||||||
 | 
								"http_port": 8080,
 | 
				
			||||||
 | 
								"https_port": 8443,
 | 
				
			||||||
 | 
								"servers": {
 | 
				
			||||||
 | 
									"srv0": {
 | 
				
			||||||
 | 
										"listen": [
 | 
				
			||||||
 | 
											":80"
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"tls": {
 | 
				
			||||||
 | 
								"automation": {
 | 
				
			||||||
 | 
									"policies": [
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											"issuer": {
 | 
				
			||||||
 | 
												"module": "internal"
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									],
 | 
				
			||||||
 | 
									"on_demand": {
 | 
				
			||||||
 | 
										"rate_limit": {
 | 
				
			||||||
 | 
											"interval": 30000000000,
 | 
				
			||||||
 | 
											"burst": 20
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"ask": "https://example.com"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}`)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user