mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 06:03:48 +03: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
4 changed files with 130 additions and 41 deletions
|
@ -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…
Reference in a new issue