caddyfile: Normalize & flatten all unmarshalers (#6037)

This commit is contained in:
Francis Lavoie 2024-01-23 19:36:59 -05:00 committed by GitHub
parent 54823f52bc
commit 750d0b8331
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 3026 additions and 3013 deletions

View file

@ -92,30 +92,26 @@ func FormattingDifference(filename string, body []byte) (caddyconfig.Warning, bo
}, true }, true
} }
// Unmarshaler is a type that can unmarshal // Unmarshaler is a type that can unmarshal Caddyfile tokens to
// Caddyfile tokens to set itself up for a // set itself up for a JSON encoding. The goal of an unmarshaler
// JSON encoding. The goal of an unmarshaler // is not to set itself up for actual use, but to set itself up for
// is not to set itself up for actual use, // being marshaled into JSON. Caddyfile-unmarshaled values will not
// but to set itself up for being marshaled // be used directly; they will be encoded as JSON and then used from
// into JSON. Caddyfile-unmarshaled values // that. Implementations _may_ be able to support multiple segments
// will not be used directly; they will be // (instances of their directive or batch of tokens); typically this
// encoded as JSON and then used from that. // means wrapping parsing logic in a loop: `for d.Next() { ... }`.
// Implementations must be able to support // More commonly, only a single segment is supported, so a simple
// multiple segments (instances of their // `d.Next()` at the start should be used to consume the module
// directive or batch of tokens); typically // identifier token (directive name, etc).
// this means wrapping all token logic in
// a loop: `for d.Next() { ... }`.
type Unmarshaler interface { type Unmarshaler interface {
UnmarshalCaddyfile(d *Dispenser) error UnmarshalCaddyfile(d *Dispenser) error
} }
// ServerType is a type that can evaluate a Caddyfile and set up a caddy config. // ServerType is a type that can evaluate a Caddyfile and set up a caddy config.
type ServerType interface { type ServerType interface {
// Setup takes the server blocks which // Setup takes the server blocks which contain tokens,
// contain tokens, as well as options // as well as options (e.g. CLI flags) and creates a
// (e.g. CLI flags) and creates a Caddy // Caddy config, along with any warnings or an error.
// config, along with any warnings or
// an error.
Setup([]ServerBlock, map[string]any) (*caddy.Config, []caddyconfig.Warning, error) Setup([]ServerBlock, map[string]any) (*caddy.Config, []caddyconfig.Warning, error)
} }

File diff suppressed because it is too large Load diff

View file

@ -271,12 +271,6 @@ func (h Helper) GroupRoutes(vals []ConfigValue) {
} }
} }
// NewBindAddresses returns config values relevant to adding
// listener bind addresses to the config.
func (h Helper) NewBindAddresses(addrs []string) []ConfigValue {
return []ConfigValue{{Class: "bind", Value: addrs}}
}
// WithDispenser returns a new instance based on d. All others Helper // WithDispenser returns a new instance based on d. All others Helper
// fields are copied, so typically maps are shared with this new instance. // fields are copied, so typically maps are shared with this new instance.
func (h Helper) WithDispenser(d *caddyfile.Dispenser) Helper { func (h Helper) WithDispenser(d *caddyfile.Dispenser) Helper {

View file

@ -1381,68 +1381,73 @@ func (st *ServerType) compileEncodedMatcherSets(sblock serverBlock) ([]caddy.Mod
} }
func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.ModuleMap) error { func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.ModuleMap) error {
for d.Next() { d.Next() // advance to the first token
// this is the "name" for "named matchers"
definitionName := d.Val()
if _, ok := matchers[definitionName]; ok { // this is the "name" for "named matchers"
return fmt.Errorf("matcher is defined more than once: %s", definitionName) definitionName := d.Val()
if _, ok := matchers[definitionName]; ok {
return fmt.Errorf("matcher is defined more than once: %s", definitionName)
}
matchers[definitionName] = make(caddy.ModuleMap)
// given a matcher name and the tokens following it, parse
// the tokens as a matcher module and record it
makeMatcher := func(matcherName string, tokens []caddyfile.Token) error {
mod, err := caddy.GetModule("http.matchers." + matcherName)
if err != nil {
return fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
} }
matchers[definitionName] = make(caddy.ModuleMap) unm, ok := mod.New().(caddyfile.Unmarshaler)
if !ok {
return fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName)
}
err = unm.UnmarshalCaddyfile(caddyfile.NewDispenser(tokens))
if err != nil {
return err
}
rm, ok := unm.(caddyhttp.RequestMatcher)
if !ok {
return fmt.Errorf("matcher module '%s' is not a request matcher", matcherName)
}
matchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)
return nil
}
// given a matcher name and the tokens following it, parse // if the next token is quoted, we can assume it's not a matcher name
// the tokens as a matcher module and record it // and that it's probably an 'expression' matcher
makeMatcher := func(matcherName string, tokens []caddyfile.Token) error { if d.NextArg() {
mod, err := caddy.GetModule("http.matchers." + matcherName) if d.Token().Quoted() {
if err != nil { // since it was missing the matcher name, we insert a token
return fmt.Errorf("getting matcher module '%s': %v", matcherName, err) // in front of the expression token itself
} err := makeMatcher("expression", []caddyfile.Token{
unm, ok := mod.New().(caddyfile.Unmarshaler) {Text: "expression", File: d.File(), Line: d.Line()},
if !ok { d.Token(),
return fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName) })
}
err = unm.UnmarshalCaddyfile(caddyfile.NewDispenser(tokens))
if err != nil { if err != nil {
return err return err
} }
rm, ok := unm.(caddyhttp.RequestMatcher)
if !ok {
return fmt.Errorf("matcher module '%s' is not a request matcher", matcherName)
}
matchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)
return nil return nil
} }
// if the next token is quoted, we can assume it's not a matcher name // if it wasn't quoted, then we need to rewind after calling
// and that it's probably an 'expression' matcher // d.NextArg() so the below properly grabs the matcher name
if d.NextArg() { d.Prev()
if d.Token().Quoted() { }
err := makeMatcher("expression", []caddyfile.Token{d.Token()})
if err != nil {
return err
}
continue
}
// if it wasn't quoted, then we need to rewind after calling // in case there are multiple instances of the same matcher, concatenate
// d.NextArg() so the below properly grabs the matcher name // their tokens (we expect that UnmarshalCaddyfile should be able to
d.Prev() // handle more than one segment); otherwise, we'd overwrite other
} // instances of the matcher in this set
tokensByMatcherName := make(map[string][]caddyfile.Token)
// in case there are multiple instances of the same matcher, concatenate for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); {
// their tokens (we expect that UnmarshalCaddyfile should be able to matcherName := d.Val()
// handle more than one segment); otherwise, we'd overwrite other tokensByMatcherName[matcherName] = append(tokensByMatcherName[matcherName], d.NextSegment()...)
// instances of the matcher in this set }
tokensByMatcherName := make(map[string][]caddyfile.Token) for matcherName, tokens := range tokensByMatcherName {
for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); { err := makeMatcher(matcherName, tokens)
matcherName := d.Val() if err != nil {
tokensByMatcherName[matcherName] = append(tokensByMatcherName[matcherName], d.NextSegment()...) return err
}
for matcherName, tokens := range tokensByMatcherName {
err := makeMatcher(matcherName, tokens)
if err != nil {
return err
}
} }
} }
return nil return nil

View file

@ -62,105 +62,103 @@ func init() {
func parseOptTrue(d *caddyfile.Dispenser, _ any) (any, error) { return true, nil } func parseOptTrue(d *caddyfile.Dispenser, _ any) (any, error) { return true, nil }
func parseOptHTTPPort(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptHTTPPort(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
var httpPort int var httpPort int
for d.Next() { var httpPortStr string
var httpPortStr string if !d.AllArgs(&httpPortStr) {
if !d.AllArgs(&httpPortStr) { return 0, d.ArgErr()
return 0, d.ArgErr() }
} var err error
var err error httpPort, err = strconv.Atoi(httpPortStr)
httpPort, err = strconv.Atoi(httpPortStr) if err != nil {
if err != nil { return 0, d.Errf("converting port '%s' to integer value: %v", httpPortStr, err)
return 0, d.Errf("converting port '%s' to integer value: %v", httpPortStr, err)
}
} }
return httpPort, nil return httpPort, nil
} }
func parseOptHTTPSPort(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptHTTPSPort(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
var httpsPort int var httpsPort int
for d.Next() { var httpsPortStr string
var httpsPortStr string if !d.AllArgs(&httpsPortStr) {
if !d.AllArgs(&httpsPortStr) { return 0, d.ArgErr()
return 0, d.ArgErr() }
} var err error
var err error httpsPort, err = strconv.Atoi(httpsPortStr)
httpsPort, err = strconv.Atoi(httpsPortStr) if err != nil {
if err != nil { return 0, d.Errf("converting port '%s' to integer value: %v", httpsPortStr, err)
return 0, d.Errf("converting port '%s' to integer value: %v", httpsPortStr, err)
}
} }
return httpsPort, nil return httpsPort, nil
} }
func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
// get directive name
if !d.Next() {
return nil, d.ArgErr()
}
dirName := d.Val()
if _, ok := registeredDirectives[dirName]; !ok {
return nil, d.Errf("%s is not a registered directive", dirName)
}
// get positional token
if !d.Next() {
return nil, d.ArgErr()
}
pos := d.Val()
newOrder := directiveOrder newOrder := directiveOrder
for d.Next() { // if directive exists, first remove it
// get directive name for i, d := range newOrder {
if !d.Next() { if d == dirName {
return nil, d.ArgErr() newOrder = append(newOrder[:i], newOrder[i+1:]...)
} break
dirName := d.Val()
if _, ok := registeredDirectives[dirName]; !ok {
return nil, d.Errf("%s is not a registered directive", dirName)
} }
}
// get positional token // act on the positional
if !d.Next() { switch pos {
return nil, d.ArgErr() case "first":
} newOrder = append([]string{dirName}, newOrder...)
pos := d.Val()
// if directive exists, first remove it
for i, d := range newOrder {
if d == dirName {
newOrder = append(newOrder[:i], newOrder[i+1:]...)
break
}
}
// act on the positional
switch pos {
case "first":
newOrder = append([]string{dirName}, newOrder...)
if d.NextArg() {
return nil, d.ArgErr()
}
directiveOrder = newOrder
return newOrder, nil
case "last":
newOrder = append(newOrder, dirName)
if d.NextArg() {
return nil, d.ArgErr()
}
directiveOrder = newOrder
return newOrder, nil
case "before":
case "after":
default:
return nil, d.Errf("unknown positional '%s'", pos)
}
// get name of other directive
if !d.NextArg() {
return nil, d.ArgErr()
}
otherDir := d.Val()
if d.NextArg() { if d.NextArg() {
return nil, d.ArgErr() return nil, d.ArgErr()
} }
directiveOrder = newOrder
return newOrder, nil
case "last":
newOrder = append(newOrder, dirName)
if d.NextArg() {
return nil, d.ArgErr()
}
directiveOrder = newOrder
return newOrder, nil
case "before":
case "after":
default:
return nil, d.Errf("unknown positional '%s'", pos)
}
// insert directive into proper position // get name of other directive
for i, d := range newOrder { if !d.NextArg() {
if d == otherDir { return nil, d.ArgErr()
if pos == "before" { }
newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...) otherDir := d.Val()
} else if pos == "after" { if d.NextArg() {
newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...) return nil, d.ArgErr()
} }
break
// insert directive into proper position
for i, d := range newOrder {
if d == otherDir {
if pos == "before" {
newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...)
} else if pos == "after" {
newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...)
} }
break
} }
} }
@ -223,57 +221,58 @@ func parseOptACMEDNS(d *caddyfile.Dispenser, _ any) (any, error) {
func parseOptACMEEAB(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptACMEEAB(d *caddyfile.Dispenser, _ any) (any, error) {
eab := new(acme.EAB) eab := new(acme.EAB)
for d.Next() { d.Next() // consume option name
if d.NextArg() { if d.NextArg() {
return nil, d.ArgErr() return nil, d.ArgErr()
} }
for nesting := d.Nesting(); d.NextBlock(nesting); { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "key_id": case "key_id":
if !d.NextArg() { if !d.NextArg() {
return nil, d.ArgErr() return nil, d.ArgErr()
}
eab.KeyID = d.Val()
case "mac_key":
if !d.NextArg() {
return nil, d.ArgErr()
}
eab.MACKey = d.Val()
default:
return nil, d.Errf("unrecognized parameter '%s'", d.Val())
} }
eab.KeyID = d.Val()
case "mac_key":
if !d.NextArg() {
return nil, d.ArgErr()
}
eab.MACKey = d.Val()
default:
return nil, d.Errf("unrecognized parameter '%s'", d.Val())
} }
} }
return eab, nil return eab, nil
} }
func parseOptCertIssuer(d *caddyfile.Dispenser, existing any) (any, error) { func parseOptCertIssuer(d *caddyfile.Dispenser, existing any) (any, error) {
d.Next() // consume option name
var issuers []certmagic.Issuer var issuers []certmagic.Issuer
if existing != nil { if existing != nil {
issuers = existing.([]certmagic.Issuer) issuers = existing.([]certmagic.Issuer)
} }
for d.Next() { // consume option name
if !d.Next() { // get issuer module name // get issuer module name
return nil, d.ArgErr() if !d.Next() {
} return nil, d.ArgErr()
modID := "tls.issuance." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return nil, err
}
iss, ok := unm.(certmagic.Issuer)
if !ok {
return nil, d.Errf("module %s (%T) is not a certmagic.Issuer", modID, unm)
}
issuers = append(issuers, iss)
} }
modID := "tls.issuance." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return nil, err
}
iss, ok := unm.(certmagic.Issuer)
if !ok {
return nil, d.Errf("module %s (%T) is not a certmagic.Issuer", modID, unm)
}
issuers = append(issuers, iss)
return issuers, nil return issuers, nil
} }
func parseOptSingleString(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptSingleString(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume parameter name d.Next() // consume option name
if !d.Next() { if !d.Next() {
return "", d.ArgErr() return "", d.ArgErr()
} }
@ -285,7 +284,7 @@ func parseOptSingleString(d *caddyfile.Dispenser, _ any) (any, error) {
} }
func parseOptStringList(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptStringList(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume parameter name d.Next() // consume option name
val := d.RemainingArgs() val := d.RemainingArgs()
if len(val) == 0 { if len(val) == 0 {
return "", d.ArgErr() return "", d.ArgErr()
@ -294,33 +293,33 @@ func parseOptStringList(d *caddyfile.Dispenser, _ any) (any, error) {
} }
func parseOptAdmin(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptAdmin(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
adminCfg := new(caddy.AdminConfig) adminCfg := new(caddy.AdminConfig)
for d.Next() { if d.NextArg() {
if d.NextArg() { listenAddress := d.Val()
listenAddress := d.Val() if listenAddress == "off" {
if listenAddress == "off" { adminCfg.Disabled = true
adminCfg.Disabled = true if d.Next() { // Do not accept any remaining options including block
if d.Next() { // Do not accept any remaining options including block return nil, d.Err("No more option is allowed after turning off admin config")
return nil, d.Err("No more option is allowed after turning off admin config") }
} } else {
} else { adminCfg.Listen = listenAddress
adminCfg.Listen = listenAddress if d.NextArg() { // At most 1 arg is allowed
if d.NextArg() { // At most 1 arg is allowed return nil, d.ArgErr()
return nil, d.ArgErr()
}
} }
} }
for nesting := d.Nesting(); d.NextBlock(nesting); { }
switch d.Val() { for d.NextBlock(0) {
case "enforce_origin": switch d.Val() {
adminCfg.EnforceOrigin = true case "enforce_origin":
adminCfg.EnforceOrigin = true
case "origins": case "origins":
adminCfg.Origins = d.RemainingArgs() adminCfg.Origins = d.RemainingArgs()
default: default:
return nil, d.Errf("unrecognized parameter '%s'", d.Val()) return nil, d.Errf("unrecognized parameter '%s'", d.Val())
}
} }
} }
if adminCfg.Listen == "" && !adminCfg.Disabled { if adminCfg.Listen == "" && !adminCfg.Disabled {
@ -330,57 +329,57 @@ func parseOptAdmin(d *caddyfile.Dispenser, _ any) (any, error) {
} }
func parseOptOnDemand(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptOnDemand(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
if d.NextArg() {
return nil, d.ArgErr()
}
var ond *caddytls.OnDemandConfig var ond *caddytls.OnDemandConfig
for d.Next() { for d.NextBlock(0) {
if d.NextArg() { switch d.Val() {
return nil, d.ArgErr() case "ask":
} if !d.NextArg() {
for nesting := d.Nesting(); d.NextBlock(nesting); { return nil, d.ArgErr()
switch d.Val() {
case "ask":
if !d.NextArg() {
return nil, d.ArgErr()
}
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
ond.Ask = d.Val()
case "interval":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, err
}
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
if ond.RateLimit == nil {
ond.RateLimit = new(caddytls.RateLimit)
}
ond.RateLimit.Interval = caddy.Duration(dur)
case "burst":
if !d.NextArg() {
return nil, d.ArgErr()
}
burst, err := strconv.Atoi(d.Val())
if err != nil {
return nil, err
}
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
if ond.RateLimit == nil {
ond.RateLimit = new(caddytls.RateLimit)
}
ond.RateLimit.Burst = burst
default:
return nil, d.Errf("unrecognized parameter '%s'", d.Val())
} }
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
ond.Ask = d.Val()
case "interval":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, err
}
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
if ond.RateLimit == nil {
ond.RateLimit = new(caddytls.RateLimit)
}
ond.RateLimit.Interval = caddy.Duration(dur)
case "burst":
if !d.NextArg() {
return nil, d.ArgErr()
}
burst, err := strconv.Atoi(d.Val())
if err != nil {
return nil, err
}
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
if ond.RateLimit == nil {
ond.RateLimit = new(caddytls.RateLimit)
}
ond.RateLimit.Burst = burst
default:
return nil, d.Errf("unrecognized parameter '%s'", d.Val())
} }
} }
if ond == nil { if ond == nil {
@ -390,7 +389,7 @@ func parseOptOnDemand(d *caddyfile.Dispenser, _ any) (any, error) {
} }
func parseOptPersistConfig(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptPersistConfig(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume parameter name d.Next() // consume option name
if !d.Next() { if !d.Next() {
return "", d.ArgErr() return "", d.ArgErr()
} }
@ -405,7 +404,7 @@ func parseOptPersistConfig(d *caddyfile.Dispenser, _ any) (any, error) {
} }
func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume parameter name d.Next() // consume option name
if !d.Next() { if !d.Next() {
return "", d.ArgErr() return "", d.ArgErr()
} }

View file

@ -48,124 +48,124 @@ func init() {
// //
// When the CA ID is unspecified, 'local' is assumed. // When the CA ID is unspecified, 'local' is assumed.
func parsePKIApp(d *caddyfile.Dispenser, existingVal any) (any, error) { func parsePKIApp(d *caddyfile.Dispenser, existingVal any) (any, error) {
pki := &caddypki.PKI{CAs: make(map[string]*caddypki.CA)} d.Next() // consume app name
for d.Next() { pki := &caddypki.PKI{
for nesting := d.Nesting(); d.NextBlock(nesting); { CAs: make(map[string]*caddypki.CA),
switch d.Val() { }
case "ca": for d.NextBlock(0) {
pkiCa := new(caddypki.CA) switch d.Val() {
case "ca":
pkiCa := new(caddypki.CA)
if d.NextArg() {
pkiCa.ID = d.Val()
if d.NextArg() { if d.NextArg() {
pkiCa.ID = d.Val() return nil, d.ArgErr()
if d.NextArg() { }
}
if pkiCa.ID == "" {
pkiCa.ID = caddypki.DefaultCAID
}
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "name":
if !d.NextArg() {
return nil, d.ArgErr() return nil, d.ArgErr()
} }
} pkiCa.Name = d.Val()
if pkiCa.ID == "" {
pkiCa.ID = caddypki.DefaultCAID
}
for nesting := d.Nesting(); d.NextBlock(nesting); { case "root_cn":
switch d.Val() { if !d.NextArg() {
case "name": return nil, d.ArgErr()
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Name = d.Val()
case "root_cn":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.RootCommonName = d.Val()
case "intermediate_cn":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.IntermediateCommonName = d.Val()
case "intermediate_lifetime":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, err
}
pkiCa.IntermediateLifetime = caddy.Duration(dur)
case "root":
if pkiCa.Root == nil {
pkiCa.Root = new(caddypki.KeyPair)
}
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "cert":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Root.Certificate = d.Val()
case "key":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Root.PrivateKey = d.Val()
case "format":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Root.Format = d.Val()
default:
return nil, d.Errf("unrecognized pki ca root option '%s'", d.Val())
}
}
case "intermediate":
if pkiCa.Intermediate == nil {
pkiCa.Intermediate = new(caddypki.KeyPair)
}
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "cert":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Intermediate.Certificate = d.Val()
case "key":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Intermediate.PrivateKey = d.Val()
case "format":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Intermediate.Format = d.Val()
default:
return nil, d.Errf("unrecognized pki ca intermediate option '%s'", d.Val())
}
}
default:
return nil, d.Errf("unrecognized pki ca option '%s'", d.Val())
} }
pkiCa.RootCommonName = d.Val()
case "intermediate_cn":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.IntermediateCommonName = d.Val()
case "intermediate_lifetime":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, err
}
pkiCa.IntermediateLifetime = caddy.Duration(dur)
case "root":
if pkiCa.Root == nil {
pkiCa.Root = new(caddypki.KeyPair)
}
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "cert":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Root.Certificate = d.Val()
case "key":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Root.PrivateKey = d.Val()
case "format":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Root.Format = d.Val()
default:
return nil, d.Errf("unrecognized pki ca root option '%s'", d.Val())
}
}
case "intermediate":
if pkiCa.Intermediate == nil {
pkiCa.Intermediate = new(caddypki.KeyPair)
}
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "cert":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Intermediate.Certificate = d.Val()
case "key":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Intermediate.PrivateKey = d.Val()
case "format":
if !d.NextArg() {
return nil, d.ArgErr()
}
pkiCa.Intermediate.Format = d.Val()
default:
return nil, d.Errf("unrecognized pki ca intermediate option '%s'", d.Val())
}
}
default:
return nil, d.Errf("unrecognized pki ca option '%s'", d.Val())
} }
pki.CAs[pkiCa.ID] = pkiCa
default:
return nil, d.Errf("unrecognized pki option '%s'", d.Val())
} }
pki.CAs[pkiCa.ID] = pkiCa
default:
return nil, d.Errf("unrecognized pki option '%s'", d.Val())
} }
} }
return pki, nil return pki, nil
} }

View file

@ -53,235 +53,235 @@ type serverOptions struct {
} }
func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) { func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
d.Next() // consume option name
serverOpts := serverOptions{} serverOpts := serverOptions{}
for d.Next() { if d.NextArg() {
serverOpts.ListenerAddress = d.Val()
if d.NextArg() { if d.NextArg() {
serverOpts.ListenerAddress = d.Val() return nil, d.ArgErr()
if d.NextArg() { }
}
for d.NextBlock(0) {
switch d.Val() {
case "name":
if serverOpts.ListenerAddress == "" {
return nil, d.Errf("cannot set a name for a server without a listener address")
}
if !d.NextArg() {
return nil, d.ArgErr() return nil, d.ArgErr()
} }
} serverOpts.Name = d.Val()
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "name":
if serverOpts.ListenerAddress == "" {
return nil, d.Errf("cannot set a name for a server without a listener address")
}
if !d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.Name = d.Val()
case "listener_wrappers": case "listener_wrappers":
for nesting := d.Nesting(); d.NextBlock(nesting); { for nesting := d.Nesting(); d.NextBlock(nesting); {
modID := "caddy.listeners." + d.Val() modID := "caddy.listeners." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return nil, err
}
listenerWrapper, ok := unm.(caddy.ListenerWrapper)
if !ok {
return nil, fmt.Errorf("module %s (%T) is not a listener wrapper", modID, unm)
}
jsonListenerWrapper := caddyconfig.JSONModuleObject(
listenerWrapper,
"wrapper",
listenerWrapper.(caddy.Module).CaddyModule().ID.Name(),
nil,
)
serverOpts.ListenerWrappersRaw = append(serverOpts.ListenerWrappersRaw, jsonListenerWrapper)
}
case "timeouts":
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "read_body":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing read_body timeout duration: %v", err)
}
serverOpts.ReadTimeout = caddy.Duration(dur)
case "read_header":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing read_header timeout duration: %v", err)
}
serverOpts.ReadHeaderTimeout = caddy.Duration(dur)
case "write":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing write timeout duration: %v", err)
}
serverOpts.WriteTimeout = caddy.Duration(dur)
case "idle":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing idle timeout duration: %v", err)
}
serverOpts.IdleTimeout = caddy.Duration(dur)
default:
return nil, d.Errf("unrecognized timeouts option '%s'", d.Val())
}
}
case "keepalive_interval":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing keepalive interval duration: %v", err)
}
serverOpts.KeepAliveInterval = caddy.Duration(dur)
case "max_header_size":
var sizeStr string
if !d.AllArgs(&sizeStr) {
return nil, d.ArgErr()
}
size, err := humanize.ParseBytes(sizeStr)
if err != nil {
return nil, d.Errf("parsing max_header_size: %v", err)
}
serverOpts.MaxHeaderBytes = int(size)
case "enable_full_duplex":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.EnableFullDuplex = true
case "log_credentials":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.ShouldLogCredentials = true
case "protocols":
protos := d.RemainingArgs()
for _, proto := range protos {
if proto != "h1" && proto != "h2" && proto != "h2c" && proto != "h3" {
return nil, d.Errf("unknown protocol '%s': expected h1, h2, h2c, or h3", proto)
}
if sliceContains(serverOpts.Protocols, proto) {
return nil, d.Errf("protocol %s specified more than once", proto)
}
serverOpts.Protocols = append(serverOpts.Protocols, proto)
}
if nesting := d.Nesting(); d.NextBlock(nesting) {
return nil, d.ArgErr()
}
case "strict_sni_host":
if d.NextArg() && d.Val() != "insecure_off" && d.Val() != "on" {
return nil, d.Errf("strict_sni_host only supports 'on' or 'insecure_off', got '%s'", d.Val())
}
boolVal := true
if d.Val() == "insecure_off" {
boolVal = false
}
serverOpts.StrictSNIHost = &boolVal
case "trusted_proxies":
if !d.NextArg() {
return nil, d.Err("trusted_proxies expects an IP range source module name as its first argument")
}
modID := "http.ip_sources." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID) unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
source, ok := unm.(caddyhttp.IPRangeSource) listenerWrapper, ok := unm.(caddy.ListenerWrapper)
if !ok { if !ok {
return nil, fmt.Errorf("module %s (%T) is not an IP range source", modID, unm) return nil, fmt.Errorf("module %s (%T) is not a listener wrapper", modID, unm)
} }
jsonSource := caddyconfig.JSONModuleObject( jsonListenerWrapper := caddyconfig.JSONModuleObject(
source, listenerWrapper,
"source", "wrapper",
source.(caddy.Module).CaddyModule().ID.Name(), listenerWrapper.(caddy.Module).CaddyModule().ID.Name(),
nil, nil,
) )
serverOpts.TrustedProxiesRaw = jsonSource serverOpts.ListenerWrappersRaw = append(serverOpts.ListenerWrappersRaw, jsonListenerWrapper)
case "trusted_proxies_strict":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.TrustedProxiesStrict = 1
case "client_ip_headers":
headers := d.RemainingArgs()
for _, header := range headers {
if sliceContains(serverOpts.ClientIPHeaders, header) {
return nil, d.Errf("client IP header %s specified more than once", header)
}
serverOpts.ClientIPHeaders = append(serverOpts.ClientIPHeaders, header)
}
if nesting := d.Nesting(); d.NextBlock(nesting) {
return nil, d.ArgErr()
}
case "metrics":
if d.NextArg() {
return nil, d.ArgErr()
}
if nesting := d.Nesting(); d.NextBlock(nesting) {
return nil, d.ArgErr()
}
serverOpts.Metrics = new(caddyhttp.Metrics)
// TODO: DEPRECATED. (August 2022)
case "protocol":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol sub-option will be removed soon")
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "allow_h2c":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: allow_h2c will be removed soon; use protocols option instead")
if d.NextArg() {
return nil, d.ArgErr()
}
if sliceContains(serverOpts.Protocols, "h2c") {
return nil, d.Errf("protocol h2c already specified")
}
serverOpts.Protocols = append(serverOpts.Protocols, "h2c")
case "strict_sni_host":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol > strict_sni_host in this position will be removed soon; move up to the servers block instead")
if d.NextArg() && d.Val() != "insecure_off" && d.Val() != "on" {
return nil, d.Errf("strict_sni_host only supports 'on' or 'insecure_off', got '%s'", d.Val())
}
boolVal := true
if d.Val() == "insecure_off" {
boolVal = false
}
serverOpts.StrictSNIHost = &boolVal
default:
return nil, d.Errf("unrecognized protocol option '%s'", d.Val())
}
}
default:
return nil, d.Errf("unrecognized servers option '%s'", d.Val())
} }
case "timeouts":
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "read_body":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing read_body timeout duration: %v", err)
}
serverOpts.ReadTimeout = caddy.Duration(dur)
case "read_header":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing read_header timeout duration: %v", err)
}
serverOpts.ReadHeaderTimeout = caddy.Duration(dur)
case "write":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing write timeout duration: %v", err)
}
serverOpts.WriteTimeout = caddy.Duration(dur)
case "idle":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing idle timeout duration: %v", err)
}
serverOpts.IdleTimeout = caddy.Duration(dur)
default:
return nil, d.Errf("unrecognized timeouts option '%s'", d.Val())
}
}
case "keepalive_interval":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing keepalive interval duration: %v", err)
}
serverOpts.KeepAliveInterval = caddy.Duration(dur)
case "max_header_size":
var sizeStr string
if !d.AllArgs(&sizeStr) {
return nil, d.ArgErr()
}
size, err := humanize.ParseBytes(sizeStr)
if err != nil {
return nil, d.Errf("parsing max_header_size: %v", err)
}
serverOpts.MaxHeaderBytes = int(size)
case "enable_full_duplex":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.EnableFullDuplex = true
case "log_credentials":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.ShouldLogCredentials = true
case "protocols":
protos := d.RemainingArgs()
for _, proto := range protos {
if proto != "h1" && proto != "h2" && proto != "h2c" && proto != "h3" {
return nil, d.Errf("unknown protocol '%s': expected h1, h2, h2c, or h3", proto)
}
if sliceContains(serverOpts.Protocols, proto) {
return nil, d.Errf("protocol %s specified more than once", proto)
}
serverOpts.Protocols = append(serverOpts.Protocols, proto)
}
if nesting := d.Nesting(); d.NextBlock(nesting) {
return nil, d.ArgErr()
}
case "strict_sni_host":
if d.NextArg() && d.Val() != "insecure_off" && d.Val() != "on" {
return nil, d.Errf("strict_sni_host only supports 'on' or 'insecure_off', got '%s'", d.Val())
}
boolVal := true
if d.Val() == "insecure_off" {
boolVal = false
}
serverOpts.StrictSNIHost = &boolVal
case "trusted_proxies":
if !d.NextArg() {
return nil, d.Err("trusted_proxies expects an IP range source module name as its first argument")
}
modID := "http.ip_sources." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return nil, err
}
source, ok := unm.(caddyhttp.IPRangeSource)
if !ok {
return nil, fmt.Errorf("module %s (%T) is not an IP range source", modID, unm)
}
jsonSource := caddyconfig.JSONModuleObject(
source,
"source",
source.(caddy.Module).CaddyModule().ID.Name(),
nil,
)
serverOpts.TrustedProxiesRaw = jsonSource
case "trusted_proxies_strict":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.TrustedProxiesStrict = 1
case "client_ip_headers":
headers := d.RemainingArgs()
for _, header := range headers {
if sliceContains(serverOpts.ClientIPHeaders, header) {
return nil, d.Errf("client IP header %s specified more than once", header)
}
serverOpts.ClientIPHeaders = append(serverOpts.ClientIPHeaders, header)
}
if nesting := d.Nesting(); d.NextBlock(nesting) {
return nil, d.ArgErr()
}
case "metrics":
if d.NextArg() {
return nil, d.ArgErr()
}
if nesting := d.Nesting(); d.NextBlock(nesting) {
return nil, d.ArgErr()
}
serverOpts.Metrics = new(caddyhttp.Metrics)
// TODO: DEPRECATED. (August 2022)
case "protocol":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol sub-option will be removed soon")
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "allow_h2c":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: allow_h2c will be removed soon; use protocols option instead")
if d.NextArg() {
return nil, d.ArgErr()
}
if sliceContains(serverOpts.Protocols, "h2c") {
return nil, d.Errf("protocol h2c already specified")
}
serverOpts.Protocols = append(serverOpts.Protocols, "h2c")
case "strict_sni_host":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol > strict_sni_host in this position will be removed soon; move up to the servers block instead")
if d.NextArg() && d.Val() != "insecure_off" && d.Val() != "on" {
return nil, d.Errf("strict_sni_host only supports 'on' or 'insecure_off', got '%s'", d.Val())
}
boolVal := true
if d.Val() == "insecure_off" {
boolVal = false
}
serverOpts.StrictSNIHost = &boolVal
default:
return nil, d.Errf("unrecognized protocol option '%s'", d.Val())
}
}
default:
return nil, d.Errf("unrecognized servers option '%s'", d.Val())
} }
} }
return serverOpts, nil return serverOpts, nil

View file

@ -0,0 +1,78 @@
:80
push * /foo.txt
push {
GET /foo.txt
}
push {
GET /foo.txt
HEAD /foo.txt
}
push {
headers {
Foo bar
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"routes": [
{
"handle": [
{
"handler": "push",
"resources": [
{
"target": "/foo.txt"
}
]
},
{
"handler": "push",
"resources": [
{
"method": "GET",
"target": "/foo.txt"
}
]
},
{
"handler": "push",
"resources": [
{
"method": "GET",
"target": "/foo.txt"
},
{
"method": "HEAD",
"target": "/foo.txt"
}
]
},
{
"handler": "push",
"headers": {
"set": {
"Foo": [
"bar"
]
}
}
}
]
}
]
}
}
}
}
}

View file

@ -40,14 +40,8 @@ func init() {
// //
// If <event> is *, then it will bind to all events. // If <event> is *, then it will bind to all events.
func parseApp(d *caddyfile.Dispenser, _ any) (any, error) { func parseApp(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
app := new(caddyevents.App) app := new(caddyevents.App)
// consume the option name
if !d.Next() {
return nil, d.ArgErr()
}
// handle the block
for d.NextBlock(0) { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "on": case "on":

View file

@ -34,56 +34,56 @@ func init() {
// //
// If no hash algorithm is supplied, bcrypt will be assumed. // If no hash algorithm is supplied, bcrypt will be assumed.
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
h.Next() // consume directive name
var ba HTTPBasicAuth var ba HTTPBasicAuth
ba.HashCache = new(Cache) ba.HashCache = new(Cache)
for h.Next() { var cmp Comparer
var cmp Comparer args := h.RemainingArgs()
args := h.RemainingArgs()
var hashName string var hashName string
switch len(args) { switch len(args) {
case 0: case 0:
hashName = "bcrypt" hashName = "bcrypt"
case 1: case 1:
hashName = args[0] hashName = args[0]
case 2: case 2:
hashName = args[0] hashName = args[0]
ba.Realm = args[1] ba.Realm = args[1]
default: default:
return nil, h.ArgErr()
}
switch hashName {
case "bcrypt":
cmp = BcryptHash{}
case "scrypt":
cmp = ScryptHash{}
default:
return nil, h.Errf("unrecognized hash algorithm: %s", hashName)
}
ba.HashRaw = caddyconfig.JSONModuleObject(cmp, "algorithm", hashName, nil)
for h.NextBlock(0) {
username := h.Val()
var b64Pwd, b64Salt string
h.Args(&b64Pwd, &b64Salt)
if h.NextArg() {
return nil, h.ArgErr() return nil, h.ArgErr()
} }
switch hashName { if username == "" || b64Pwd == "" {
case "bcrypt": return nil, h.Err("username and password cannot be empty or missing")
cmp = BcryptHash{}
case "scrypt":
cmp = ScryptHash{}
default:
return nil, h.Errf("unrecognized hash algorithm: %s", hashName)
} }
ba.HashRaw = caddyconfig.JSONModuleObject(cmp, "algorithm", hashName, nil) ba.AccountList = append(ba.AccountList, Account{
Username: username,
for h.NextBlock(0) { Password: b64Pwd,
username := h.Val() Salt: b64Salt,
})
var b64Pwd, b64Salt string
h.Args(&b64Pwd, &b64Salt)
if h.NextArg() {
return nil, h.ArgErr()
}
if username == "" || b64Pwd == "" {
return nil, h.Err("username and password cannot be empty or missing")
}
ba.AccountList = append(ba.AccountList, Account{
Username: username,
Password: b64Pwd,
Salt: b64Salt,
})
}
} }
return Authentication{ return Authentication{

View file

@ -176,13 +176,27 @@ func (m MatchExpression) Match(r *http.Request) bool {
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchExpression) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *MatchExpression) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume matcher name
if d.CountRemainingArgs() > 1 {
m.Expr = strings.Join(d.RemainingArgsRaw(), " ") // if there's multiple args, then we need to keep the raw
} else { // tokens because the user may have used quotes within their
m.Expr = d.Val() // CEL expression (e.g. strings) and we should retain that
} if d.CountRemainingArgs() > 1 {
m.Expr = strings.Join(d.RemainingArgsRaw(), " ")
return nil
} }
// there should at least be one arg
if !d.NextArg() {
return d.ArgErr()
}
// if there's only one token, then we can safely grab the
// cleaned token (no quotes) and use that as the expression
// because there's no valid CEL expression that is only a
// quoted string; commonly quotes are used in Caddyfile to
// define the expression
m.Expr = d.Val()
return nil return nil
} }

View file

@ -54,62 +54,60 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
// //
// Specifying the formats on the first line will use those formats' defaults. // Specifying the formats on the first line will use those formats' defaults.
func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
var prefer []string d.Next() // consume directive name
prefer := []string{}
for _, arg := range d.RemainingArgs() {
mod, err := caddy.GetModule("http.encoders." + arg)
if err != nil {
return d.Errf("finding encoder module '%s': %v", mod, err)
}
encoding, ok := mod.New().(Encoding)
if !ok {
return d.Errf("module %s is not an HTTP encoding", mod)
}
if enc.EncodingsRaw == nil {
enc.EncodingsRaw = make(caddy.ModuleMap)
}
enc.EncodingsRaw[arg] = caddyconfig.JSON(encoding, nil)
prefer = append(prefer, arg)
}
responseMatchers := make(map[string]caddyhttp.ResponseMatcher) responseMatchers := make(map[string]caddyhttp.ResponseMatcher)
for d.NextBlock(0) {
for d.Next() { switch d.Val() {
for _, arg := range d.RemainingArgs() { case "minimum_length":
mod, err := caddy.GetModule("http.encoders." + arg) if !d.NextArg() {
if err != nil { return d.ArgErr()
return d.Errf("finding encoder module '%s': %v", mod, err)
} }
encoding, ok := mod.New().(Encoding) minLength, err := strconv.Atoi(d.Val())
if err != nil {
return err
}
enc.MinLength = minLength
case "match":
err := caddyhttp.ParseNamedResponseMatcher(d.NewFromNextSegment(), responseMatchers)
if err != nil {
return err
}
matcher := responseMatchers["match"]
enc.Matcher = &matcher
default:
name := d.Val()
modID := "http.encoders." + name
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return err
}
encoding, ok := unm.(Encoding)
if !ok { if !ok {
return d.Errf("module %s is not an HTTP encoding", mod) return d.Errf("module %s is not an HTTP encoding; is %T", modID, unm)
} }
if enc.EncodingsRaw == nil { if enc.EncodingsRaw == nil {
enc.EncodingsRaw = make(caddy.ModuleMap) enc.EncodingsRaw = make(caddy.ModuleMap)
} }
enc.EncodingsRaw[arg] = caddyconfig.JSON(encoding, nil) enc.EncodingsRaw[name] = caddyconfig.JSON(encoding, nil)
prefer = append(prefer, arg) prefer = append(prefer, name)
}
for d.NextBlock(0) {
switch d.Val() {
case "minimum_length":
if !d.NextArg() {
return d.ArgErr()
}
minLength, err := strconv.Atoi(d.Val())
if err != nil {
return err
}
enc.MinLength = minLength
case "match":
err := caddyhttp.ParseNamedResponseMatcher(d.NewFromNextSegment(), responseMatchers)
if err != nil {
return err
}
matcher := responseMatchers["match"]
enc.Matcher = &matcher
default:
name := d.Val()
modID := "http.encoders." + name
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return err
}
encoding, ok := unm.(Encoding)
if !ok {
return d.Errf("module %s is not an HTTP encoding; is %T", modID, unm)
}
if enc.EncodingsRaw == nil {
enc.EncodingsRaw = make(caddy.ModuleMap)
}
enc.EncodingsRaw[name] = caddyconfig.JSON(encoding, nil)
prefer = append(prefer, name)
}
} }
} }

View file

@ -44,17 +44,16 @@ func (Gzip) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. // UnmarshalCaddyfile sets up the handler from Caddyfile tokens.
func (g *Gzip) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (g *Gzip) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume option name
if !d.NextArg() { if !d.NextArg() {
continue return nil
}
levelStr := d.Val()
level, err := strconv.Atoi(levelStr)
if err != nil {
return err
}
g.Level = level
} }
levelStr := d.Val()
level, err := strconv.Atoi(levelStr)
if err != nil {
return err
}
g.Level = level
return nil return nil
} }

View file

@ -223,7 +223,7 @@ func parseTryFiles(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
// parse out the optional try policy // parse out the optional try policy
var tryPolicy string var tryPolicy string
for nesting := h.Nesting(); h.NextBlock(nesting); { for h.NextBlock(0) {
switch h.Val() { switch h.Val() {
case "policy": case "policy":
if tryPolicy != "" { if tryPolicy != "" {

View file

@ -127,6 +127,7 @@ func (MatchFile) CaddyModule() caddy.ModuleInfo {
// try_policy first_exist|smallest_size|largest_size|most_recently_modified // try_policy first_exist|smallest_size|largest_size|most_recently_modified
// } // }
func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
m.TryFiles = append(m.TryFiles, d.RemainingArgs()...) m.TryFiles = append(m.TryFiles, d.RemainingArgs()...)
for d.NextBlock(0) { for d.NextBlock(0) {

View file

@ -47,14 +47,12 @@ func init() {
// ? conditionally sets a value only if the header field is not already set, // ? conditionally sets a value only if the header field is not already set,
// and > sets a field with defer enabled. // and > sets a field with defer enabled.
func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) { func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
if !h.Next() { h.Next() // consume directive name
return nil, h.ArgErr()
}
matcherSet, err := h.ExtractMatcherSet() matcherSet, err := h.ExtractMatcherSet()
if err != nil { if err != nil {
return nil, err return nil, err
} }
h.Next() // consume the directive name again (matcher parsing resets)
makeHandler := func() Handler { makeHandler := func() Handler {
return Handler{ return Handler{
@ -65,73 +63,71 @@ func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
} }
handler, handlerWithRequire := makeHandler(), makeHandler() handler, handlerWithRequire := makeHandler(), makeHandler()
for h.Next() { // first see if headers are in the initial line
// first see if headers are in the initial line var hasArgs bool
var hasArgs bool if h.NextArg() {
hasArgs = true
field := h.Val()
var value, replacement string
if h.NextArg() { if h.NextArg() {
hasArgs = true value = h.Val()
field := h.Val() }
var value, replacement string if h.NextArg() {
if h.NextArg() { replacement = h.Val()
value = h.Val() }
} err := applyHeaderOp(
if h.NextArg() { handler.Response.HeaderOps,
replacement = h.Val() handler.Response,
} field,
err := applyHeaderOp( value,
handler.Response.HeaderOps, replacement,
handler.Response, )
field, if err != nil {
value, return nil, h.Err(err.Error())
replacement, }
) if len(handler.Response.HeaderOps.Delete) > 0 {
if err != nil { handler.Response.Deferred = true
return nil, h.Err(err.Error()) }
} }
if len(handler.Response.HeaderOps.Delete) > 0 {
handler.Response.Deferred = true // if not, they should be in a block
} for h.NextBlock(0) {
field := h.Val()
if field == "defer" {
handler.Response.Deferred = true
continue
}
if hasArgs {
return nil, h.Err("cannot specify headers in both arguments and block") // because it would be weird
} }
// if not, they should be in a block // sometimes it is habitual for users to suffix a field name with a colon,
for h.NextBlock(0) { // as if they were writing a curl command or something; see
field := h.Val() // https://caddy.community/t/v2-reverse-proxy-please-add-cors-example-to-the-docs/7349/19
if field == "defer" { field = strings.TrimSuffix(field, ":")
handler.Response.Deferred = true
continue
}
if hasArgs {
return nil, h.Err("cannot specify headers in both arguments and block") // because it would be weird
}
// sometimes it is habitual for users to suffix a field name with a colon, var value, replacement string
// as if they were writing a curl command or something; see if h.NextArg() {
// https://caddy.community/t/v2-reverse-proxy-please-add-cors-example-to-the-docs/7349/19 value = h.Val()
field = strings.TrimSuffix(field, ":") }
if h.NextArg() {
replacement = h.Val()
}
var value, replacement string handlerToUse := handler
if h.NextArg() { if strings.HasPrefix(field, "?") {
value = h.Val() handlerToUse = handlerWithRequire
} }
if h.NextArg() {
replacement = h.Val()
}
handlerToUse := handler err := applyHeaderOp(
if strings.HasPrefix(field, "?") { handlerToUse.Response.HeaderOps,
handlerToUse = handlerWithRequire handlerToUse.Response,
} field,
value,
err := applyHeaderOp( replacement,
handlerToUse.Response.HeaderOps, )
handlerToUse.Response, if err != nil {
field, return nil, h.Err(err.Error())
value,
replacement,
)
if err != nil {
return nil, h.Err(err.Error())
}
} }
} }
@ -151,56 +147,52 @@ func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
// //
// request_header [<matcher>] [[+|-]<field> [<value|regexp>] [<replacement>]] // request_header [<matcher>] [[+|-]<field> [<value|regexp>] [<replacement>]]
func parseReqHdrCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) { func parseReqHdrCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
if !h.Next() { h.Next() // consume directive name
return nil, h.ArgErr()
}
matcherSet, err := h.ExtractMatcherSet() matcherSet, err := h.ExtractMatcherSet()
if err != nil { if err != nil {
return nil, err return nil, err
} }
h.Next() // consume the directive name again (matcher parsing resets)
configValues := []httpcaddyfile.ConfigValue{} configValues := []httpcaddyfile.ConfigValue{}
for h.Next() { if !h.NextArg() {
if !h.NextArg() { return nil, h.ArgErr()
return nil, h.ArgErr() }
} field := h.Val()
field := h.Val()
hdr := Handler{ hdr := Handler{
Request: &HeaderOps{}, Request: &HeaderOps{},
} }
// sometimes it is habitual for users to suffix a field name with a colon, // sometimes it is habitual for users to suffix a field name with a colon,
// as if they were writing a curl command or something; see // as if they were writing a curl command or something; see
// https://caddy.community/t/v2-reverse-proxy-please-add-cors-example-to-the-docs/7349/19 // https://caddy.community/t/v2-reverse-proxy-please-add-cors-example-to-the-docs/7349/19
field = strings.TrimSuffix(field, ":") field = strings.TrimSuffix(field, ":")
var value, replacement string
if h.NextArg() {
value = h.Val()
}
if h.NextArg() {
replacement = h.Val()
if h.NextArg() {
return nil, h.ArgErr()
}
}
if hdr.Request == nil {
hdr.Request = new(HeaderOps)
}
if err := CaddyfileHeaderOp(hdr.Request, field, value, replacement); err != nil {
return nil, h.Err(err.Error())
}
configValues = append(configValues, h.NewRoute(matcherSet, hdr)...)
var value, replacement string
if h.NextArg() {
value = h.Val()
}
if h.NextArg() {
replacement = h.Val()
if h.NextArg() { if h.NextArg() {
return nil, h.ArgErr() return nil, h.ArgErr()
} }
} }
if hdr.Request == nil {
hdr.Request = new(HeaderOps)
}
if err := CaddyfileHeaderOp(hdr.Request, field, value, replacement); err != nil {
return nil, h.Err(err.Error())
}
configValues = append(configValues, h.NewRoute(matcherSet, hdr)...)
if h.NextArg() {
return nil, h.ArgErr()
}
return configValues, nil return configValues, nil
} }

View file

@ -79,24 +79,23 @@ func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume matcher name
for d.NextArg() { for d.NextArg() {
if d.Val() == "forwarded" { if d.Val() == "forwarded" {
if len(m.Ranges) > 0 { if len(m.Ranges) > 0 {
return d.Err("if used, 'forwarded' must be first argument") return d.Err("if used, 'forwarded' must be first argument")
}
m.Forwarded = true
continue
} }
if d.Val() == "private_ranges" { m.Forwarded = true
m.Ranges = append(m.Ranges, PrivateRangesCIDR()...) continue
continue
}
m.Ranges = append(m.Ranges, d.Val())
} }
if d.NextBlock(0) { if d.Val() == "private_ranges" {
return d.Err("malformed remote_ip matcher: blocks are not supported") m.Ranges = append(m.Ranges, PrivateRangesCIDR()...)
continue
} }
m.Ranges = append(m.Ranges, d.Val())
}
if d.NextBlock(0) {
return d.Err("malformed remote_ip matcher: blocks are not supported")
} }
return nil return nil
} }
@ -189,17 +188,16 @@ func (MatchClientIP) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchClientIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *MatchClientIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume matcher name
for d.NextArg() { for d.NextArg() {
if d.Val() == "private_ranges" { if d.Val() == "private_ranges" {
m.Ranges = append(m.Ranges, PrivateRangesCIDR()...) m.Ranges = append(m.Ranges, PrivateRangesCIDR()...)
continue continue
}
m.Ranges = append(m.Ranges, d.Val())
}
if d.NextBlock(0) {
return d.Err("malformed client_ip matcher: blocks are not supported")
} }
m.Ranges = append(m.Ranges, d.Val())
}
if d.NextBlock(0) {
return d.Err("malformed client_ip matcher: blocks are not supported")
} }
return nil return nil
} }

View file

@ -42,74 +42,73 @@ func init() {
// However, for convenience, there may be fewer outputs than destinations and any missing // However, for convenience, there may be fewer outputs than destinations and any missing
// outputs will be filled in implicitly. // outputs will be filled in implicitly.
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
h.Next() // consume directive name
var handler Handler var handler Handler
for h.Next() { // source
// source if !h.NextArg() {
if !h.NextArg() { return nil, h.ArgErr()
return nil, h.ArgErr() }
} handler.Source = h.Val()
handler.Source = h.Val()
// destinations // destinations
handler.Destinations = h.RemainingArgs() handler.Destinations = h.RemainingArgs()
if len(handler.Destinations) == 0 { if len(handler.Destinations) == 0 {
return nil, h.Err("missing destination argument(s)") return nil, h.Err("missing destination argument(s)")
} }
for _, dest := range handler.Destinations { for _, dest := range handler.Destinations {
if shorthand := httpcaddyfile.WasReplacedPlaceholderShorthand(dest); shorthand != "" { if shorthand := httpcaddyfile.WasReplacedPlaceholderShorthand(dest); shorthand != "" {
return nil, h.Errf("destination %s conflicts with a Caddyfile placeholder shorthand", shorthand) return nil, h.Errf("destination %s conflicts with a Caddyfile placeholder shorthand", shorthand)
}
}
// mappings
for h.NextBlock(0) {
// defaults are a special case
if h.Val() == "default" {
if len(handler.Defaults) > 0 {
return nil, h.Err("defaults already defined")
}
handler.Defaults = h.RemainingArgs()
for len(handler.Defaults) < len(handler.Destinations) {
handler.Defaults = append(handler.Defaults, "")
}
continue
}
// every line maps an input value to one or more outputs
in := h.Val()
var outs []any
for h.NextArg() {
val := h.ScalarVal()
if val == "-" {
outs = append(outs, nil)
} else {
outs = append(outs, val)
}
}
// cannot have more outputs than destinations
if len(outs) > len(handler.Destinations) {
return nil, h.Err("too many outputs")
}
// for convenience, can have fewer outputs than destinations, but the
// underlying handler won't accept that, so we fill in nil values
for len(outs) < len(handler.Destinations) {
outs = append(outs, nil)
}
// create the mapping
mapping := Mapping{Outputs: outs}
if strings.HasPrefix(in, "~") {
mapping.InputRegexp = in[1:]
} else {
mapping.Input = in
}
handler.Mappings = append(handler.Mappings, mapping)
} }
} }
// mappings
for h.NextBlock(0) {
// defaults are a special case
if h.Val() == "default" {
if len(handler.Defaults) > 0 {
return nil, h.Err("defaults already defined")
}
handler.Defaults = h.RemainingArgs()
for len(handler.Defaults) < len(handler.Destinations) {
handler.Defaults = append(handler.Defaults, "")
}
continue
}
// every line maps an input value to one or more outputs
in := h.Val()
var outs []any
for h.NextArg() {
val := h.ScalarVal()
if val == "-" {
outs = append(outs, nil)
} else {
outs = append(outs, val)
}
}
// cannot have more outputs than destinations
if len(outs) > len(handler.Destinations) {
return nil, h.Err("too many outputs")
}
// for convenience, can have fewer outputs than destinations, but the
// underlying handler won't accept that, so we fill in nil values
for len(outs) < len(handler.Destinations) {
outs = append(outs, nil)
}
// create the mapping
mapping := Mapping{Outputs: outs}
if strings.HasPrefix(in, "~") {
mapping.InputRegexp = in[1:]
} else {
mapping.Input = in
}
handler.Mappings = append(handler.Mappings, mapping)
}
return handler, nil return handler, nil
} }

View file

@ -225,6 +225,7 @@ func (MatchHost) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchHost) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *MatchHost) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
*m = append(*m, d.RemainingArgs()...) *m = append(*m, d.RemainingArgs()...)
if d.NextBlock(0) { if d.NextBlock(0) {
@ -632,6 +633,7 @@ func (MatchPath) CELLibrary(ctx caddy.Context) (cel.Library, error) {
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchPath) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *MatchPath) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
*m = append(*m, d.RemainingArgs()...) *m = append(*m, d.RemainingArgs()...)
if d.NextBlock(0) { if d.NextBlock(0) {
@ -716,6 +718,7 @@ func (MatchMethod) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchMethod) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *MatchMethod) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
*m = append(*m, d.RemainingArgs()...) *m = append(*m, d.RemainingArgs()...)
if d.NextBlock(0) { if d.NextBlock(0) {
@ -770,6 +773,7 @@ func (m *MatchQuery) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if *m == nil { if *m == nil {
*m = make(map[string][]string) *m = make(map[string][]string)
} }
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
for _, query := range d.RemainingArgs() { for _, query := range d.RemainingArgs() {
if query == "" { if query == "" {
@ -868,6 +872,7 @@ func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if *m == nil { if *m == nil {
*m = make(map[string][]string) *m = make(map[string][]string)
} }
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
var field, val string var field, val string
if !d.Args(&field) { if !d.Args(&field) {
@ -1002,6 +1007,7 @@ func (m *MatchHeaderRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if *m == nil { if *m == nil {
*m = make(map[string]*MatchRegexp) *m = make(map[string]*MatchRegexp)
} }
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
var first, second, third string var first, second, third string
if !d.Args(&first, &second) { if !d.Args(&first, &second) {
@ -1166,6 +1172,7 @@ func (m MatchProtocol) Match(r *http.Request) bool {
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchProtocol) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *MatchProtocol) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
var proto string var proto string
if !d.Args(&proto) { if !d.Args(&proto) {
@ -1207,6 +1214,7 @@ func (MatchNot) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchNot) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *MatchNot) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
matcherSet, err := ParseCaddyfileNestedMatcherSet(d) matcherSet, err := ParseCaddyfileNestedMatcherSet(d)
if err != nil { if err != nil {
@ -1331,6 +1339,7 @@ func (mre *MatchRegexp) Match(input string, repl *caddy.Replacer) bool {
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (mre *MatchRegexp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (mre *MatchRegexp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
// If this is the second iteration of the loop // If this is the second iteration of the loop
// then there's more than one path_regexp matcher // then there's more than one path_regexp matcher

View file

@ -39,40 +39,40 @@ func (ListenerWrapper) CaddyModule() caddy.ModuleInfo {
// fallback_policy <policy> // fallback_policy <policy>
// } // }
func (w *ListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (w *ListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume wrapper name
// No same-line options are supported
if d.NextArg() {
return d.ArgErr()
}
for d.NextBlock(0) { // No same-line options are supported
switch d.Val() { if d.NextArg() {
case "timeout": return d.ArgErr()
if !d.NextArg() { }
return d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("parsing proxy_protocol timeout duration: %v", err)
}
w.Timeout = caddy.Duration(dur)
case "allow": for d.NextBlock(0) {
w.Allow = append(w.Allow, d.RemainingArgs()...) switch d.Val() {
case "deny": case "timeout":
w.Deny = append(w.Deny, d.RemainingArgs()...) if !d.NextArg() {
case "fallback_policy":
if !d.NextArg() {
return d.ArgErr()
}
p, err := parsePolicy(d.Val())
if err != nil {
return d.WrapErr(err)
}
w.FallbackPolicy = p
default:
return d.ArgErr() return d.ArgErr()
} }
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("parsing proxy_protocol timeout duration: %v", err)
}
w.Timeout = caddy.Duration(dur)
case "allow":
w.Allow = append(w.Allow, d.RemainingArgs()...)
case "deny":
w.Deny = append(w.Deny, d.RemainingArgs()...)
case "fallback_policy":
if !d.NextArg() {
return d.ArgErr()
}
p, err := parsePolicy(d.Val())
if err != nil {
return d.WrapErr(err)
}
w.FallbackPolicy = p
default:
return d.ArgErr()
} }
} }
return nil return nil

View file

@ -44,63 +44,63 @@ func init() {
// Placeholders are accepted in resource and header field // Placeholders are accepted in resource and header field
// name and value and replacement tokens. // name and value and replacement tokens.
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
h.Next() // consume directive name
handler := new(Handler) handler := new(Handler)
for h.Next() { // inline resources
if h.NextArg() { if h.NextArg() {
handler.Resources = append(handler.Resources, Resource{Target: h.Val()}) handler.Resources = append(handler.Resources, Resource{Target: h.Val()})
}
// optional block
for outerNesting := h.Nesting(); h.NextBlock(outerNesting); {
switch h.Val() {
case "headers":
if h.NextArg() {
return nil, h.ArgErr()
}
for innerNesting := h.Nesting(); h.NextBlock(innerNesting); {
var err error
// include current token, which we treat as an argument here
args := []string{h.Val()}
args = append(args, h.RemainingArgs()...)
if handler.Headers == nil {
handler.Headers = new(HeaderConfig)
}
switch len(args) {
case 1:
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], "", "")
case 2:
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], args[1], "")
case 3:
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], args[1], args[2])
default:
return nil, h.ArgErr()
}
if err != nil {
return nil, h.Err(err.Error())
}
}
case "GET", "HEAD":
method := h.Val()
if !h.NextArg() {
return nil, h.ArgErr()
}
target := h.Val()
handler.Resources = append(handler.Resources, Resource{
Method: method,
Target: target,
})
default:
handler.Resources = append(handler.Resources, Resource{Target: h.Val()})
}
}
} }
// optional block
for h.NextBlock(0) {
switch h.Val() {
case "headers":
if h.NextArg() {
return nil, h.ArgErr()
}
for nesting := h.Nesting(); h.NextBlock(nesting); {
var err error
// include current token, which we treat as an argument here
args := []string{h.Val()}
args = append(args, h.RemainingArgs()...)
if handler.Headers == nil {
handler.Headers = new(HeaderConfig)
}
switch len(args) {
case 1:
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], "", "")
case 2:
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], args[1], "")
case 3:
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], args[1], args[2])
default:
return nil, h.ArgErr()
}
if err != nil {
return nil, h.Err(err.Error())
}
}
case "GET", "HEAD":
method := h.Val()
if !h.NextArg() {
return nil, h.ArgErr()
}
target := h.Val()
handler.Resources = append(handler.Resources, Resource{
Method: method,
Target: target,
})
default:
handler.Resources = append(handler.Resources, Resource{Target: h.Val()})
}
}
return handler, nil return handler, nil
} }

View file

@ -26,25 +26,26 @@ func init() {
} }
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
h.Next() // consume directive name
rb := new(RequestBody) rb := new(RequestBody)
for h.Next() { // configuration should be in a block
// configuration should be in a block for h.NextBlock(0) {
for h.NextBlock(0) { switch h.Val() {
switch h.Val() { case "max_size":
case "max_size": var sizeStr string
var sizeStr string if !h.AllArgs(&sizeStr) {
if !h.AllArgs(&sizeStr) { return nil, h.ArgErr()
return nil, h.ArgErr()
}
size, err := humanize.ParseBytes(sizeStr)
if err != nil {
return nil, h.Errf("parsing max_size: %v", err)
}
rb.MaxSize = int64(size)
default:
return nil, h.Errf("unrecognized servers option '%s'", h.Val())
} }
size, err := humanize.ParseBytes(sizeStr)
if err != nil {
return nil, h.Errf("parsing max_size: %v", err)
}
rb.MaxSize = int64(size)
default:
return nil, h.Errf("unrecognized servers option '%s'", h.Val())
} }
} }

View file

@ -67,55 +67,53 @@ func (rm ResponseMatcher) matchStatusCode(statusCode int) bool {
// //
// @name [header <field> [<value>]] | [status <code...>] // @name [header <field> [<value>]] | [status <code...>]
func ParseNamedResponseMatcher(d *caddyfile.Dispenser, matchers map[string]ResponseMatcher) error { func ParseNamedResponseMatcher(d *caddyfile.Dispenser, matchers map[string]ResponseMatcher) error {
for d.Next() { d.Next() // consume matcher name
definitionName := d.Val() definitionName := d.Val()
if _, ok := matchers[definitionName]; ok { if _, ok := matchers[definitionName]; ok {
return d.Errf("matcher is defined more than once: %s", definitionName) return d.Errf("matcher is defined more than once: %s", definitionName)
}
matcher := ResponseMatcher{}
for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); {
switch d.Val() {
case "header":
if matcher.Headers == nil {
matcher.Headers = http.Header{}
}
// reuse the header request matcher's unmarshaler
headerMatcher := MatchHeader(matcher.Headers)
err := headerMatcher.UnmarshalCaddyfile(d.NewFromNextSegment())
if err != nil {
return err
}
matcher.Headers = http.Header(headerMatcher)
case "status":
if matcher.StatusCode == nil {
matcher.StatusCode = []int{}
}
args := d.RemainingArgs()
if len(args) == 0 {
return d.ArgErr()
}
for _, arg := range args {
if len(arg) == 3 && strings.HasSuffix(arg, "xx") {
arg = arg[:1]
}
statusNum, err := strconv.Atoi(arg)
if err != nil {
return d.Errf("bad status value '%s': %v", arg, err)
}
matcher.StatusCode = append(matcher.StatusCode, statusNum)
}
default:
return d.Errf("unrecognized response matcher %s", d.Val())
}
}
matchers[definitionName] = matcher
} }
matcher := ResponseMatcher{}
for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); {
switch d.Val() {
case "header":
if matcher.Headers == nil {
matcher.Headers = http.Header{}
}
// reuse the header request matcher's unmarshaler
headerMatcher := MatchHeader(matcher.Headers)
err := headerMatcher.UnmarshalCaddyfile(d.NewFromNextSegment())
if err != nil {
return err
}
matcher.Headers = http.Header(headerMatcher)
case "status":
if matcher.StatusCode == nil {
matcher.StatusCode = []int{}
}
args := d.RemainingArgs()
if len(args) == 0 {
return d.ArgErr()
}
for _, arg := range args {
if len(arg) == 3 && strings.HasSuffix(arg, "xx") {
arg = arg[:1]
}
statusNum, err := strconv.Atoi(arg)
if err != nil {
return d.Errf("bad status value '%s': %v", arg, err)
}
matcher.StatusCode = append(matcher.StatusCode, statusNum)
}
default:
return d.Errf("unrecognized response matcher %s", d.Val())
}
}
matchers[definitionName] = matcher
return nil return nil
} }

File diff suppressed because it is too large Load diff

View file

@ -46,76 +46,75 @@ func init() {
// capture_stderr // capture_stderr
// } // }
func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume transport name
for d.NextBlock(0) { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "root": case "root":
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
t.Root = d.Val()
case "split":
t.SplitPath = d.RemainingArgs()
if len(t.SplitPath) == 0 {
return d.ArgErr()
}
case "env":
args := d.RemainingArgs()
if len(args) != 2 {
return d.ArgErr()
}
if t.EnvVars == nil {
t.EnvVars = make(map[string]string)
}
t.EnvVars[args[0]] = args[1]
case "resolve_root_symlink":
if d.NextArg() {
return d.ArgErr()
}
t.ResolveRootSymlink = true
case "dial_timeout":
if !d.NextArg() {
return d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("bad timeout value %s: %v", d.Val(), err)
}
t.DialTimeout = caddy.Duration(dur)
case "read_timeout":
if !d.NextArg() {
return d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("bad timeout value %s: %v", d.Val(), err)
}
t.ReadTimeout = caddy.Duration(dur)
case "write_timeout":
if !d.NextArg() {
return d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("bad timeout value %s: %v", d.Val(), err)
}
t.WriteTimeout = caddy.Duration(dur)
case "capture_stderr":
if d.NextArg() {
return d.ArgErr()
}
t.CaptureStderr = true
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
t.Root = d.Val()
case "split":
t.SplitPath = d.RemainingArgs()
if len(t.SplitPath) == 0 {
return d.ArgErr()
}
case "env":
args := d.RemainingArgs()
if len(args) != 2 {
return d.ArgErr()
}
if t.EnvVars == nil {
t.EnvVars = make(map[string]string)
}
t.EnvVars[args[0]] = args[1]
case "resolve_root_symlink":
if d.NextArg() {
return d.ArgErr()
}
t.ResolveRootSymlink = true
case "dial_timeout":
if !d.NextArg() {
return d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("bad timeout value %s: %v", d.Val(), err)
}
t.DialTimeout = caddy.Duration(dur)
case "read_timeout":
if !d.NextArg() {
return d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("bad timeout value %s: %v", d.Val(), err)
}
t.ReadTimeout = caddy.Duration(dur)
case "write_timeout":
if !d.NextArg() {
return d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("bad timeout value %s: %v", d.Val(), err)
}
t.WriteTimeout = caddy.Duration(dur)
case "capture_stderr":
if d.NextArg() {
return d.ArgErr()
}
t.CaptureStderr = true
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
} }
return nil return nil

View file

@ -68,10 +68,9 @@ func (r RandomSelection) Select(pool UpstreamPool, request *http.Request, _ http
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (r *RandomSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (r *RandomSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
} }
return nil return nil
} }
@ -98,22 +97,22 @@ func (WeightedRoundRobinSelection) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (r *WeightedRoundRobinSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (r *WeightedRoundRobinSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
args := d.RemainingArgs()
if len(args) == 0 {
return d.ArgErr()
}
for _, weight := range args { args := d.RemainingArgs()
weightInt, err := strconv.Atoi(weight) if len(args) == 0 {
if err != nil { return d.ArgErr()
return d.Errf("invalid weight value '%s': %v", weight, err) }
}
if weightInt < 1 { for _, weight := range args {
return d.Errf("invalid weight value '%s': weight should be non-zero and positive", weight) weightInt, err := strconv.Atoi(weight)
} if err != nil {
r.Weights = append(r.Weights, weightInt) return d.Errf("invalid weight value '%s': %v", weight, err)
} }
if weightInt < 1 {
return d.Errf("invalid weight value '%s': weight should be non-zero and positive", weight)
}
r.Weights = append(r.Weights, weightInt)
} }
return nil return nil
} }
@ -179,17 +178,17 @@ func (RandomChoiceSelection) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (r *RandomChoiceSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (r *RandomChoiceSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if !d.NextArg() {
return d.ArgErr() if !d.NextArg() {
} return d.ArgErr()
chooseStr := d.Val()
choose, err := strconv.Atoi(chooseStr)
if err != nil {
return d.Errf("invalid choice value '%s': %v", chooseStr, err)
}
r.Choose = choose
} }
chooseStr := d.Val()
choose, err := strconv.Atoi(chooseStr)
if err != nil {
return d.Errf("invalid choice value '%s': %v", chooseStr, err)
}
r.Choose = choose
return nil return nil
} }
@ -280,10 +279,9 @@ func (LeastConnSelection) Select(pool UpstreamPool, _ *http.Request, _ http.Resp
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (r *LeastConnSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (r *LeastConnSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
} }
return nil return nil
} }
@ -320,10 +318,9 @@ func (r *RoundRobinSelection) Select(pool UpstreamPool, _ *http.Request, _ http.
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (r *RoundRobinSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (r *RoundRobinSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
} }
return nil return nil
} }
@ -352,10 +349,9 @@ func (FirstSelection) Select(pool UpstreamPool, _ *http.Request, _ http.Response
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (r *FirstSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (r *FirstSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
} }
return nil return nil
} }
@ -383,10 +379,9 @@ func (IPHashSelection) Select(pool UpstreamPool, req *http.Request, _ http.Respo
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (r *IPHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (r *IPHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
} }
return nil return nil
} }
@ -416,10 +411,9 @@ func (ClientIPHashSelection) Select(pool UpstreamPool, req *http.Request, _ http
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (r *ClientIPHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (r *ClientIPHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
} }
return nil return nil
} }
@ -443,10 +437,9 @@ func (URIHashSelection) Select(pool UpstreamPool, req *http.Request, _ http.Resp
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (r *URIHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (r *URIHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
} }
return nil return nil
} }
@ -504,13 +497,14 @@ func (s QueryHashSelection) Select(pool UpstreamPool, req *http.Request, _ http.
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (s *QueryHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (s *QueryHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if !d.NextArg() {
return d.ArgErr() if !d.NextArg() {
} return d.ArgErr()
s.Key = d.Val()
} }
for nesting := d.Nesting(); d.NextBlock(nesting); { s.Key = d.Val()
for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "fallback": case "fallback":
if !d.NextArg() { if !d.NextArg() {
@ -583,13 +577,14 @@ func (s HeaderHashSelection) Select(pool UpstreamPool, req *http.Request, _ http
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (s *HeaderHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (s *HeaderHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume policy name
if !d.NextArg() {
return d.ArgErr() if !d.NextArg() {
} return d.ArgErr()
s.Field = d.Val()
} }
for nesting := d.Nesting(); d.NextBlock(nesting); { s.Field = d.Val()
for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "fallback": case "fallback":
if !d.NextArg() { if !d.NextArg() {
@ -708,7 +703,7 @@ func (s *CookieHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
default: default:
return d.ArgErr() return d.ArgErr()
} }
for nesting := d.Nesting(); d.NextBlock(nesting); { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "fallback": case "fallback":
if !d.NextArg() { if !d.NextArg() {

View file

@ -39,10 +39,7 @@ func init() {
// Only URI components which are given in <to> will be set in the resulting URI. // Only URI components which are given in <to> will be set in the resulting URI.
// See the docs for the rewrite handler for more information. // See the docs for the rewrite handler for more information.
func parseCaddyfileRewrite(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) { func parseCaddyfileRewrite(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
// consume directive name h.Next() // consume directive name
if !h.NextArg() {
return nil, h.ArgErr()
}
// count the tokens to determine what to do // count the tokens to determine what to do
argsCount := h.CountRemainingArgs() argsCount := h.CountRemainingArgs()
@ -66,26 +63,9 @@ func parseCaddyfileRewrite(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue,
if err != nil { if err != nil {
return nil, err return nil, err
} }
h.Next() // consume directive name again, matcher parsing does a reset
h.Next() // advance to the rewrite URI
// consume directive name, again, because extracting matcher does a reset
if !h.NextArg() {
return nil, h.ArgErr()
}
// advance to the rewrite URI
if !h.NextArg() {
return nil, h.ArgErr()
}
var rewr Rewrite
for h.Next() {
if !h.NextArg() {
return nil, h.ArgErr()
}
rewr.URI = h.Val()
if h.NextArg() {
return nil, h.ArgErr()
}
}
return h.NewRoute(userMatcherSet, Rewrite{URI: h.Val()}), nil return h.NewRoute(userMatcherSet, Rewrite{URI: h.Val()}), nil
} }
@ -93,17 +73,14 @@ func parseCaddyfileRewrite(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue,
// //
// method [<matcher>] <method> // method [<matcher>] <method>
func parseCaddyfileMethod(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { func parseCaddyfileMethod(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
var rewr Rewrite h.Next() // consume directive name
for h.Next() { if !h.NextArg() {
if !h.NextArg() { return nil, h.ArgErr()
return nil, h.ArgErr()
}
rewr.Method = h.Val()
if h.NextArg() {
return nil, h.ArgErr()
}
} }
return rewr, nil if h.NextArg() {
return nil, h.ArgErr()
}
return Rewrite{Method: h.Val()}, nil
} }
// parseCaddyfileURI sets up a handler for manipulating (but not "rewriting") the // parseCaddyfileURI sets up a handler for manipulating (but not "rewriting") the
@ -118,65 +95,71 @@ func parseCaddyfileMethod(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler,
// path_regexp is used, then regular expression replacements will be performed // path_regexp is used, then regular expression replacements will be performed
// on the path portion of the URI (and a limit cannot be set). // on the path portion of the URI (and a limit cannot be set).
func parseCaddyfileURI(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { func parseCaddyfileURI(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
h.Next() // consume directive name
args := h.RemainingArgs()
if len(args) < 2 {
return nil, h.ArgErr()
}
var rewr Rewrite var rewr Rewrite
for h.Next() {
args := h.RemainingArgs() switch args[0] {
if len(args) < 2 { case "strip_prefix":
if len(args) > 2 {
return nil, h.ArgErr() return nil, h.ArgErr()
} }
switch args[0] { rewr.StripPathPrefix = args[1]
case "strip_prefix": if !strings.HasPrefix(rewr.StripPathPrefix, "/") {
if len(args) > 2 { rewr.StripPathPrefix = "/" + rewr.StripPathPrefix
return nil, h.ArgErr()
}
rewr.StripPathPrefix = args[1]
if !strings.HasPrefix(rewr.StripPathPrefix, "/") {
rewr.StripPathPrefix = "/" + rewr.StripPathPrefix
}
case "strip_suffix":
if len(args) > 2 {
return nil, h.ArgErr()
}
rewr.StripPathSuffix = args[1]
case "replace":
var find, replace, lim string
switch len(args) {
case 4:
lim = args[3]
fallthrough
case 3:
find = args[1]
replace = args[2]
default:
return nil, h.ArgErr()
}
var limInt int
if lim != "" {
var err error
limInt, err = strconv.Atoi(lim)
if err != nil {
return nil, h.Errf("limit must be an integer; invalid: %v", err)
}
}
rewr.URISubstring = append(rewr.URISubstring, substrReplacer{
Find: find,
Replace: replace,
Limit: limInt,
})
case "path_regexp":
if len(args) != 3 {
return nil, h.ArgErr()
}
find, replace := args[1], args[2]
rewr.PathRegexp = append(rewr.PathRegexp, &regexReplacer{
Find: find,
Replace: replace,
})
default:
return nil, h.Errf("unrecognized URI manipulation '%s'", args[0])
} }
case "strip_suffix":
if len(args) > 2 {
return nil, h.ArgErr()
}
rewr.StripPathSuffix = args[1]
case "replace":
var find, replace, lim string
switch len(args) {
case 4:
lim = args[3]
fallthrough
case 3:
find = args[1]
replace = args[2]
default:
return nil, h.ArgErr()
}
var limInt int
if lim != "" {
var err error
limInt, err = strconv.Atoi(lim)
if err != nil {
return nil, h.Errf("limit must be an integer; invalid: %v", err)
}
}
rewr.URISubstring = append(rewr.URISubstring, substrReplacer{
Find: find,
Replace: replace,
Limit: limInt,
})
case "path_regexp":
if len(args) != 3 {
return nil, h.ArgErr()
}
find, replace := args[1], args[2]
rewr.PathRegexp = append(rewr.PathRegexp, &regexReplacer{
Find: find,
Replace: replace,
})
default:
return nil, h.Errf("unrecognized URI manipulation '%s'", args[0])
} }
return rewr, nil return rewr, nil
} }
@ -190,9 +173,9 @@ func parseCaddyfileURI(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, err
// Only path matchers (with a `/` prefix) are supported as this is a shortcut // Only path matchers (with a `/` prefix) are supported as this is a shortcut
// for the handle directive with a strip_prefix rewrite. // for the handle directive with a strip_prefix rewrite.
func parseCaddyfileHandlePath(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) { func parseCaddyfileHandlePath(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
if !h.Next() { h.Next() // consume directive name
return nil, h.ArgErr()
} // there must be a path matcher
if !h.NextArg() { if !h.NextArg() {
return nil, h.ArgErr() return nil, h.ArgErr()
} }

View file

@ -60,36 +60,35 @@ func (StaticError) CaddyModule() caddy.ModuleInfo {
// If there is just one argument (other than the matcher), it is considered // If there is just one argument (other than the matcher), it is considered
// to be a status code if it's a valid positive integer of 3 digits. // to be a status code if it's a valid positive integer of 3 digits.
func (e *StaticError) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (e *StaticError) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume directive name
args := d.RemainingArgs() args := d.RemainingArgs()
switch len(args) { switch len(args) {
case 1: case 1:
if len(args[0]) == 3 { if len(args[0]) == 3 {
if num, err := strconv.Atoi(args[0]); err == nil && num > 0 { if num, err := strconv.Atoi(args[0]); err == nil && num > 0 {
e.StatusCode = WeakString(args[0]) e.StatusCode = WeakString(args[0])
break break
}
} }
e.Error = args[0]
case 2:
e.Error = args[0]
e.StatusCode = WeakString(args[1])
default:
return d.ArgErr()
} }
e.Error = args[0]
case 2:
e.Error = args[0]
e.StatusCode = WeakString(args[1])
default:
return d.ArgErr()
}
for d.NextBlock(0) { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "message": case "message":
if e.Error != "" { if e.Error != "" {
return d.Err("message already specified") return d.Err("message already specified")
}
if !d.AllArgs(&e.Error) {
return d.ArgErr()
}
default:
return d.Errf("unrecognized subdirective '%s'", d.Val())
} }
if !d.AllArgs(&e.Error) {
return d.ArgErr()
}
default:
return d.Errf("unrecognized subdirective '%s'", d.Val())
} }
} }
return nil return nil

View file

@ -138,41 +138,40 @@ func (StaticResponse) CaddyModule() caddy.ModuleInfo {
// If there is just one argument (other than the matcher), it is considered // If there is just one argument (other than the matcher), it is considered
// to be a status code if it's a valid positive integer of 3 digits. // to be a status code if it's a valid positive integer of 3 digits.
func (s *StaticResponse) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (s *StaticResponse) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume directive name
args := d.RemainingArgs() args := d.RemainingArgs()
switch len(args) { switch len(args) {
case 1: case 1:
if len(args[0]) == 3 { if len(args[0]) == 3 {
if num, err := strconv.Atoi(args[0]); err == nil && num > 0 { if num, err := strconv.Atoi(args[0]); err == nil && num > 0 {
s.StatusCode = WeakString(args[0]) s.StatusCode = WeakString(args[0])
break break
}
} }
s.Body = args[0]
case 2:
s.Body = args[0]
s.StatusCode = WeakString(args[1])
default:
return d.ArgErr()
} }
s.Body = args[0]
case 2:
s.Body = args[0]
s.StatusCode = WeakString(args[1])
default:
return d.ArgErr()
}
for d.NextBlock(0) { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "body": case "body":
if s.Body != "" { if s.Body != "" {
return d.Err("body already specified") return d.Err("body already specified")
}
if !d.AllArgs(&s.Body) {
return d.ArgErr()
}
case "close":
if s.Close {
return d.Err("close already specified")
}
s.Close = true
default:
return d.Errf("unrecognized subdirective '%s'", d.Val())
} }
if !d.AllArgs(&s.Body) {
return d.ArgErr()
}
case "close":
if s.Close {
return d.Err("close already specified")
}
s.Close = true
default:
return d.Errf("unrecognized subdirective '%s'", d.Val())
} }
} }
return nil return nil

View file

@ -34,47 +34,46 @@ func init() {
// root <path> // root <path>
// } // }
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
h.Next() // consume directive name
t := new(Templates) t := new(Templates)
for h.Next() { for h.NextBlock(0) {
for h.NextBlock(0) { switch h.Val() {
switch h.Val() { case "mime":
case "mime": t.MIMETypes = h.RemainingArgs()
t.MIMETypes = h.RemainingArgs() if len(t.MIMETypes) == 0 {
if len(t.MIMETypes) == 0 { return nil, h.ArgErr()
return nil, h.ArgErr() }
case "between":
t.Delimiters = h.RemainingArgs()
if len(t.Delimiters) != 2 {
return nil, h.ArgErr()
}
case "root":
if !h.Args(&t.FileRoot) {
return nil, h.ArgErr()
}
case "extensions":
if h.NextArg() {
return nil, h.ArgErr()
}
if t.ExtensionsRaw != nil {
return nil, h.Err("extensions already specified")
}
for nesting := h.Nesting(); h.NextBlock(nesting); {
extensionModuleName := h.Val()
modID := "http.handlers.templates.functions." + extensionModuleName
unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID)
if err != nil {
return nil, err
} }
case "between": cf, ok := unm.(CustomFunctions)
t.Delimiters = h.RemainingArgs() if !ok {
if len(t.Delimiters) != 2 { return nil, h.Errf("module %s (%T) does not provide template functions", modID, unm)
return nil, h.ArgErr()
} }
case "root": if t.ExtensionsRaw == nil {
if !h.Args(&t.FileRoot) { t.ExtensionsRaw = make(caddy.ModuleMap)
return nil, h.ArgErr()
}
case "extensions":
if h.NextArg() {
return nil, h.ArgErr()
}
if t.ExtensionsRaw != nil {
return nil, h.Err("extensions already specified")
}
for nesting := h.Nesting(); h.NextBlock(nesting); {
extensionModuleName := h.Val()
modID := "http.handlers.templates.functions." + extensionModuleName
unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID)
if err != nil {
return nil, err
}
cf, ok := unm.(CustomFunctions)
if !ok {
return nil, h.Errf("module %s (%T) does not provide template functions", modID, unm)
}
if t.ExtensionsRaw == nil {
t.ExtensionsRaw = make(caddy.ModuleMap)
}
t.ExtensionsRaw[extensionModuleName] = caddyconfig.JSON(cf, nil)
} }
t.ExtensionsRaw[extensionModuleName] = caddyconfig.JSON(cf, nil)
} }
} }
} }

View file

@ -88,20 +88,18 @@ func (ot *Tracing) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
"span": &ot.SpanName, "span": &ot.SpanName,
} }
for d.Next() { d.Next() // consume directive name
args := d.RemainingArgs() if d.NextArg() {
if len(args) > 0 { return d.ArgErr()
return d.ArgErr() }
}
for d.NextBlock(0) { for d.NextBlock(0) {
if dst, ok := paramsMap[d.Val()]; ok { if dst, ok := paramsMap[d.Val()]; ok {
if err := setParameter(d, dst); err != nil { if err := setParameter(d, dst); err != nil {
return err return err
}
} else {
return d.ArgErr()
} }
} else {
return d.ArgErr()
} }
} }
return nil return nil

View file

@ -68,6 +68,8 @@ func (m VarsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next H
// ... // ...
// } // }
func (m *VarsMiddleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *VarsMiddleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
d.Next() // consume directive name
if *m == nil { if *m == nil {
*m = make(VarsMiddleware) *m = make(VarsMiddleware)
} }
@ -94,15 +96,13 @@ func (m *VarsMiddleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return nil return nil
} }
for d.Next() { if err := nextVar(true); err != nil {
if err := nextVar(true); err != nil { return err
}
for d.NextBlock(0) {
if err := nextVar(false); err != nil {
return err return err
} }
for nesting := d.Nesting(); d.NextBlock(nesting); {
if err := nextVar(false); err != nil {
return err
}
}
} }
return nil return nil
@ -135,6 +135,7 @@ func (m *VarsMatcher) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if *m == nil { if *m == nil {
*m = make(map[string][]string) *m = make(map[string][]string)
} }
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
var field string var field string
if !d.Args(&field) { if !d.Args(&field) {
@ -216,6 +217,7 @@ func (m *MatchVarsRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if *m == nil { if *m == nil {
*m = make(map[string]*MatchRegexp) *m = make(map[string]*MatchRegexp)
} }
// iterate to merge multiple matchers into one
for d.Next() { for d.Next() {
var first, second, third string var first, second, third string
if !d.Args(&first, &second) { if !d.Args(&first, &second) {

View file

@ -34,53 +34,52 @@ func init() {
// resolvers <addresses...> // resolvers <addresses...>
// } // }
func parseACMEServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) { func parseACMEServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
if !h.Next() { h.Next() // consume directive name
return nil, h.ArgErr()
}
matcherSet, err := h.ExtractMatcherSet() matcherSet, err := h.ExtractMatcherSet()
if err != nil { if err != nil {
return nil, err return nil, err
} }
h.Next() // consume the directive name again (matcher parsing resets)
// no inline args allowed
if h.NextArg() {
return nil, h.ArgErr()
}
var acmeServer Handler var acmeServer Handler
var ca *caddypki.CA var ca *caddypki.CA
for h.Next() { for h.NextBlock(0) {
if h.NextArg() { switch h.Val() {
return nil, h.ArgErr() case "ca":
} if !h.AllArgs(&acmeServer.CA) {
for h.NextBlock(0) { return nil, h.ArgErr()
switch h.Val() { }
case "ca": if ca == nil {
if !h.AllArgs(&acmeServer.CA) { ca = new(caddypki.CA)
return nil, h.ArgErr() }
} ca.ID = acmeServer.CA
if ca == nil {
ca = new(caddypki.CA)
}
ca.ID = acmeServer.CA
case "lifetime":
if !h.NextArg() {
return nil, h.ArgErr()
}
dur, err := caddy.ParseDuration(h.Val()) case "lifetime":
if err != nil { if !h.NextArg() {
return nil, err return nil, h.ArgErr()
} }
if d := time.Duration(ca.IntermediateLifetime); d > 0 && dur > d { dur, err := caddy.ParseDuration(h.Val())
return nil, h.Errf("certificate lifetime (%s) exceeds intermediate certificate lifetime (%s)", dur, d) if err != nil {
} return nil, err
}
acmeServer.Lifetime = caddy.Duration(dur) if d := time.Duration(ca.IntermediateLifetime); d > 0 && dur > d {
return nil, h.Errf("certificate lifetime (%s) exceeds intermediate certificate lifetime (%s)", dur, d)
}
case "resolvers": acmeServer.Lifetime = caddy.Duration(dur)
acmeServer.Resolvers = h.RemainingArgs()
if len(acmeServer.Resolvers) == 0 { case "resolvers":
return nil, h.Errf("must specify at least one resolver address") acmeServer.Resolvers = h.RemainingArgs()
} if len(acmeServer.Resolvers) == 0 {
return nil, h.Errf("must specify at least one resolver address")
} }
} }
} }

View file

@ -277,218 +277,219 @@ func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }
// } // }
// } // }
func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume issuer name
if d.NextArg() {
iss.CA = d.Val()
if d.NextArg() { if d.NextArg() {
iss.CA = d.Val() return d.ArgErr()
}
}
for d.NextBlock(0) {
switch d.Val() {
case "dir":
if iss.CA != "" {
return d.Errf("directory is already specified: %s", iss.CA)
}
if !d.AllArgs(&iss.CA) {
return d.ArgErr()
}
case "test_dir":
if !d.AllArgs(&iss.TestCA) {
return d.ArgErr()
}
case "email":
if !d.AllArgs(&iss.Email) {
return d.ArgErr()
}
case "timeout":
var timeoutStr string
if !d.AllArgs(&timeoutStr) {
return d.ArgErr()
}
timeout, err := caddy.ParseDuration(timeoutStr)
if err != nil {
return d.Errf("invalid timeout duration %s: %v", timeoutStr, err)
}
iss.ACMETimeout = caddy.Duration(timeout)
case "disable_http_challenge":
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
} }
} if iss.Challenges == nil {
for nesting := d.Nesting(); d.NextBlock(nesting); { iss.Challenges = new(ChallengesConfig)
switch d.Val() {
case "dir":
if iss.CA != "" {
return d.Errf("directory is already specified: %s", iss.CA)
}
if !d.AllArgs(&iss.CA) {
return d.ArgErr()
}
case "test_dir":
if !d.AllArgs(&iss.TestCA) {
return d.ArgErr()
}
case "email":
if !d.AllArgs(&iss.Email) {
return d.ArgErr()
}
case "timeout":
var timeoutStr string
if !d.AllArgs(&timeoutStr) {
return d.ArgErr()
}
timeout, err := caddy.ParseDuration(timeoutStr)
if err != nil {
return d.Errf("invalid timeout duration %s: %v", timeoutStr, err)
}
iss.ACMETimeout = caddy.Duration(timeout)
case "disable_http_challenge":
if d.NextArg() {
return d.ArgErr()
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.HTTP == nil {
iss.Challenges.HTTP = new(HTTPChallengeConfig)
}
iss.Challenges.HTTP.Disabled = true
case "disable_tlsalpn_challenge":
if d.NextArg() {
return d.ArgErr()
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.TLSALPN == nil {
iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
}
iss.Challenges.TLSALPN.Disabled = true
case "alt_http_port":
if !d.NextArg() {
return d.ArgErr()
}
port, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("invalid port %s: %v", d.Val(), err)
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.HTTP == nil {
iss.Challenges.HTTP = new(HTTPChallengeConfig)
}
iss.Challenges.HTTP.AlternatePort = port
case "alt_tlsalpn_port":
if !d.NextArg() {
return d.ArgErr()
}
port, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("invalid port %s: %v", d.Val(), err)
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.TLSALPN == nil {
iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
}
iss.Challenges.TLSALPN.AlternatePort = port
case "eab":
iss.ExternalAccount = new(acme.EAB)
if !d.AllArgs(&iss.ExternalAccount.KeyID, &iss.ExternalAccount.MACKey) {
return d.ArgErr()
}
case "trusted_roots":
iss.TrustedRootsPEMFiles = d.RemainingArgs()
case "dns":
if !d.NextArg() {
return d.ArgErr()
}
provName := d.Val()
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
unm, err := caddyfile.UnmarshalModule(d, "dns.providers."+provName)
if err != nil {
return err
}
iss.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(unm, "name", provName, nil)
case "propagation_delay":
if !d.NextArg() {
return d.ArgErr()
}
delayStr := d.Val()
delay, err := caddy.ParseDuration(delayStr)
if err != nil {
return d.Errf("invalid propagation_delay duration %s: %v", delayStr, err)
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.PropagationDelay = caddy.Duration(delay)
case "propagation_timeout":
if !d.NextArg() {
return d.ArgErr()
}
timeoutStr := d.Val()
var timeout time.Duration
if timeoutStr == "-1" {
timeout = time.Duration(-1)
} else {
var err error
timeout, err = caddy.ParseDuration(timeoutStr)
if err != nil {
return d.Errf("invalid propagation_timeout duration %s: %v", timeoutStr, err)
}
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.PropagationTimeout = caddy.Duration(timeout)
case "resolvers":
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.Resolvers = d.RemainingArgs()
if len(iss.Challenges.DNS.Resolvers) == 0 {
return d.ArgErr()
}
case "dns_ttl":
if !d.NextArg() {
return d.ArgErr()
}
ttlStr := d.Val()
ttl, err := caddy.ParseDuration(ttlStr)
if err != nil {
return d.Errf("invalid dns_ttl duration %s: %v", ttlStr, err)
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.TTL = caddy.Duration(ttl)
case "dns_challenge_override_domain":
arg := d.RemainingArgs()
if len(arg) != 1 {
return d.ArgErr()
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.OverrideDomain = arg[0]
case "preferred_chains":
chainPref, err := ParseCaddyfilePreferredChainsOptions(d)
if err != nil {
return err
}
iss.PreferredChains = chainPref
default:
return d.Errf("unrecognized ACME issuer property: %s", d.Val())
} }
if iss.Challenges.HTTP == nil {
iss.Challenges.HTTP = new(HTTPChallengeConfig)
}
iss.Challenges.HTTP.Disabled = true
case "disable_tlsalpn_challenge":
if d.NextArg() {
return d.ArgErr()
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.TLSALPN == nil {
iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
}
iss.Challenges.TLSALPN.Disabled = true
case "alt_http_port":
if !d.NextArg() {
return d.ArgErr()
}
port, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("invalid port %s: %v", d.Val(), err)
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.HTTP == nil {
iss.Challenges.HTTP = new(HTTPChallengeConfig)
}
iss.Challenges.HTTP.AlternatePort = port
case "alt_tlsalpn_port":
if !d.NextArg() {
return d.ArgErr()
}
port, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("invalid port %s: %v", d.Val(), err)
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.TLSALPN == nil {
iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
}
iss.Challenges.TLSALPN.AlternatePort = port
case "eab":
iss.ExternalAccount = new(acme.EAB)
if !d.AllArgs(&iss.ExternalAccount.KeyID, &iss.ExternalAccount.MACKey) {
return d.ArgErr()
}
case "trusted_roots":
iss.TrustedRootsPEMFiles = d.RemainingArgs()
case "dns":
if !d.NextArg() {
return d.ArgErr()
}
provName := d.Val()
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
unm, err := caddyfile.UnmarshalModule(d, "dns.providers."+provName)
if err != nil {
return err
}
iss.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(unm, "name", provName, nil)
case "propagation_delay":
if !d.NextArg() {
return d.ArgErr()
}
delayStr := d.Val()
delay, err := caddy.ParseDuration(delayStr)
if err != nil {
return d.Errf("invalid propagation_delay duration %s: %v", delayStr, err)
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.PropagationDelay = caddy.Duration(delay)
case "propagation_timeout":
if !d.NextArg() {
return d.ArgErr()
}
timeoutStr := d.Val()
var timeout time.Duration
if timeoutStr == "-1" {
timeout = time.Duration(-1)
} else {
var err error
timeout, err = caddy.ParseDuration(timeoutStr)
if err != nil {
return d.Errf("invalid propagation_timeout duration %s: %v", timeoutStr, err)
}
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.PropagationTimeout = caddy.Duration(timeout)
case "resolvers":
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.Resolvers = d.RemainingArgs()
if len(iss.Challenges.DNS.Resolvers) == 0 {
return d.ArgErr()
}
case "dns_ttl":
if !d.NextArg() {
return d.ArgErr()
}
ttlStr := d.Val()
ttl, err := caddy.ParseDuration(ttlStr)
if err != nil {
return d.Errf("invalid dns_ttl duration %s: %v", ttlStr, err)
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.TTL = caddy.Duration(ttl)
case "dns_challenge_override_domain":
arg := d.RemainingArgs()
if len(arg) != 1 {
return d.ArgErr()
}
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.OverrideDomain = arg[0]
case "preferred_chains":
chainPref, err := ParseCaddyfilePreferredChainsOptions(d)
if err != nil {
return err
}
iss.PreferredChains = chainPref
default:
return d.Errf("unrecognized ACME issuer property: %s", d.Val())
} }
} }
return nil return nil

View file

@ -72,10 +72,9 @@ func (ts Tailscale) canHazCertificate(ctx context.Context, hello *tls.ClientHell
// //
// ... tailscale // ... tailscale
func (Tailscale) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (Tailscale) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume cert manager name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
} }
return nil return nil
} }
@ -169,17 +168,18 @@ func (hcg HTTPCertGetter) GetCertificate(ctx context.Context, hello *tls.ClientH
// //
// ... http <url> // ... http <url>
func (hcg *HTTPCertGetter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (hcg *HTTPCertGetter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume cert manager name
if !d.NextArg() {
return d.ArgErr() if !d.NextArg() {
} return d.ArgErr()
hcg.URL = d.Val() }
if d.NextArg() { hcg.URL = d.Val()
return d.ArgErr()
} if d.NextArg() {
for nesting := d.Nesting(); d.NextBlock(nesting); { return d.ArgErr()
return d.Err("block not allowed here") }
} if d.NextBlock(0) {
return d.Err("block not allowed here")
} }
return nil return nil
} }

View file

@ -155,31 +155,30 @@ func (iss InternalIssuer) Issue(ctx context.Context, csr *x509.CertificateReques
// sign_with_root // sign_with_root
// } // }
func (iss *InternalIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (iss *InternalIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume issuer name
for d.NextBlock(0) { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "ca": case "ca":
if !d.AllArgs(&iss.CA) { if !d.AllArgs(&iss.CA) {
return d.ArgErr() return d.ArgErr()
}
case "lifetime":
if !d.NextArg() {
return d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return err
}
iss.Lifetime = caddy.Duration(dur)
case "sign_with_root":
if d.NextArg() {
return d.ArgErr()
}
iss.SignWithRoot = true
} }
case "lifetime":
if !d.NextArg() {
return d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return err
}
iss.Lifetime = caddy.Duration(dur)
case "sign_with_root":
if d.NextArg() {
return d.ArgErr()
}
iss.SignWithRoot = true
} }
} }
return nil return nil

View file

@ -208,21 +208,20 @@ func (iss *ZeroSSLIssuer) Revoke(ctx context.Context, cert certmagic.Certificate
// //
// Any of the subdirectives for the ACME issuer can be used in the block. // Any of the subdirectives for the ACME issuer can be used in the block.
func (iss *ZeroSSLIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (iss *ZeroSSLIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume issuer name
if d.NextArg() {
iss.APIKey = d.Val()
if d.NextArg() { if d.NextArg() {
iss.APIKey = d.Val() return d.ArgErr()
if d.NextArg() {
return d.ArgErr()
}
} }
}
if iss.ACMEIssuer == nil { if iss.ACMEIssuer == nil {
iss.ACMEIssuer = new(ACMEIssuer) iss.ACMEIssuer = new(ACMEIssuer)
} }
err := iss.ACMEIssuer.UnmarshalCaddyfile(d.NewFromNextSegment()) err := iss.ACMEIssuer.UnmarshalCaddyfile(d.NewFromNextSegment())
if err != nil { if err != nil {
return err return err
}
} }
return nil return nil
} }

View file

@ -55,7 +55,7 @@ func (s *FileStorage) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
} }
for nesting := d.Nesting(); d.NextBlock(nesting); { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "root": case "root":
if !d.NextArg() { if !d.NextArg() {

View file

@ -65,14 +65,13 @@ func (ce *ConsoleEncoder) Provision(_ caddy.Context) error {
// See the godoc on the LogEncoderConfig type for the syntax of // See the godoc on the LogEncoderConfig type for the syntax of
// subdirectives that are common to most/all encoders. // subdirectives that are common to most/all encoders.
func (ce *ConsoleEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (ce *ConsoleEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume encoder name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
} }
err := ce.LogEncoderConfig.UnmarshalCaddyfile(d) err := ce.LogEncoderConfig.UnmarshalCaddyfile(d)
if err != nil { if err != nil {
return err return err
}
} }
return nil return nil
} }
@ -106,14 +105,13 @@ func (je *JSONEncoder) Provision(_ caddy.Context) error {
// See the godoc on the LogEncoderConfig type for the syntax of // See the godoc on the LogEncoderConfig type for the syntax of
// subdirectives that are common to most/all encoders. // subdirectives that are common to most/all encoders.
func (je *JSONEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (je *JSONEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume encoder name
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
} }
err := je.LogEncoderConfig.UnmarshalCaddyfile(d) err := je.LogEncoderConfig.UnmarshalCaddyfile(d)
if err != nil { if err != nil {
return err return err
}
} }
return nil return nil
} }
@ -149,7 +147,7 @@ type LogEncoderConfig struct {
// level_format <format> // level_format <format>
// } // }
func (lec *LogEncoderConfig) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (lec *LogEncoderConfig) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for nesting := d.Nesting(); d.NextBlock(nesting); { for d.NextBlock(0) {
subdir := d.Val() subdir := d.Val()
switch subdir { switch subdir {
case "time_local": case "time_local":

View file

@ -154,73 +154,72 @@ func (fw FileWriter) OpenWriter() (io.WriteCloser, error) {
// omitted or set to a zero value, then Caddy's default value for that // omitted or set to a zero value, then Caddy's default value for that
// subdirective is used. // subdirective is used.
func (fw *FileWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (fw *FileWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume writer name
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
} }
fw.Filename = d.Val() fw.Filename = d.Val()
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
} }
for d.NextBlock(0) { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "roll_disabled": case "roll_disabled":
var f bool var f bool
fw.Roll = &f fw.Roll = &f
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
case "roll_size":
var sizeStr string
if !d.AllArgs(&sizeStr) {
return d.ArgErr()
}
size, err := humanize.ParseBytes(sizeStr)
if err != nil {
return d.Errf("parsing size: %v", err)
}
fw.RollSizeMB = int(math.Ceil(float64(size) / humanize.MiByte))
case "roll_uncompressed":
var f bool
fw.RollCompress = &f
if d.NextArg() {
return d.ArgErr()
}
case "roll_local_time":
fw.RollLocalTime = true
if d.NextArg() {
return d.ArgErr()
}
case "roll_keep":
var keepStr string
if !d.AllArgs(&keepStr) {
return d.ArgErr()
}
keep, err := strconv.Atoi(keepStr)
if err != nil {
return d.Errf("parsing roll_keep number: %v", err)
}
fw.RollKeep = keep
case "roll_keep_for":
var keepForStr string
if !d.AllArgs(&keepForStr) {
return d.ArgErr()
}
keepFor, err := caddy.ParseDuration(keepForStr)
if err != nil {
return d.Errf("parsing roll_keep_for duration: %v", err)
}
if keepFor < 0 {
return d.Errf("negative roll_keep_for duration: %v", keepFor)
}
fw.RollKeepDays = int(math.Ceil(keepFor.Hours() / 24))
} }
case "roll_size":
var sizeStr string
if !d.AllArgs(&sizeStr) {
return d.ArgErr()
}
size, err := humanize.ParseBytes(sizeStr)
if err != nil {
return d.Errf("parsing size: %v", err)
}
fw.RollSizeMB = int(math.Ceil(float64(size) / humanize.MiByte))
case "roll_uncompressed":
var f bool
fw.RollCompress = &f
if d.NextArg() {
return d.ArgErr()
}
case "roll_local_time":
fw.RollLocalTime = true
if d.NextArg() {
return d.ArgErr()
}
case "roll_keep":
var keepStr string
if !d.AllArgs(&keepStr) {
return d.ArgErr()
}
keep, err := strconv.Atoi(keepStr)
if err != nil {
return d.Errf("parsing roll_keep number: %v", err)
}
fw.RollKeep = keep
case "roll_keep_for":
var keepForStr string
if !d.AllArgs(&keepForStr) {
return d.ArgErr()
}
keepFor, err := caddy.ParseDuration(keepForStr)
if err != nil {
return d.Errf("parsing roll_keep_for duration: %v", err)
}
if keepFor < 0 {
return d.Errf("negative roll_keep_for duration: %v", keepFor)
}
fw.RollKeepDays = int(math.Ceil(keepFor.Hours() / 24))
} }
} }
return nil return nil

View file

@ -108,50 +108,49 @@ func (fe *FilterEncoder) Provision(ctx caddy.Context) error {
// } // }
// } // }
func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume encoder name
for d.NextBlock(0) { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "wrap": case "wrap":
if !d.NextArg() {
return d.ArgErr()
}
moduleName := d.Val()
moduleID := "caddy.logging.encoders." + moduleName
unm, err := caddyfile.UnmarshalModule(d, moduleID)
if err != nil {
return err
}
enc, ok := unm.(zapcore.Encoder)
if !ok {
return d.Errf("module %s (%T) is not a zapcore.Encoder", moduleID, unm)
}
fe.WrappedRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, nil)
case "fields":
for nesting := d.Nesting(); d.NextBlock(nesting); {
field := d.Val()
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
} }
moduleName := d.Val() filterName := d.Val()
moduleID := "caddy.logging.encoders." + moduleName moduleID := "caddy.logging.encoders.filter." + filterName
unm, err := caddyfile.UnmarshalModule(d, moduleID) unm, err := caddyfile.UnmarshalModule(d, moduleID)
if err != nil { if err != nil {
return err return err
} }
enc, ok := unm.(zapcore.Encoder) filter, ok := unm.(LogFieldFilter)
if !ok { if !ok {
return d.Errf("module %s (%T) is not a zapcore.Encoder", moduleID, unm) return d.Errf("module %s (%T) is not a logging.LogFieldFilter", moduleID, unm)
} }
fe.WrappedRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, nil) if fe.FieldsRaw == nil {
fe.FieldsRaw = make(map[string]json.RawMessage)
case "fields":
for d.NextBlock(1) {
field := d.Val()
if !d.NextArg() {
return d.ArgErr()
}
filterName := d.Val()
moduleID := "caddy.logging.encoders.filter." + filterName
unm, err := caddyfile.UnmarshalModule(d, moduleID)
if err != nil {
return err
}
filter, ok := unm.(LogFieldFilter)
if !ok {
return d.Errf("module %s (%T) is not a logging.LogFieldFilter", moduleID, unm)
}
if fe.FieldsRaw == nil {
fe.FieldsRaw = make(map[string]json.RawMessage)
}
fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(filter, "filter", filterName, nil)
} }
fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(filter, "filter", filterName, nil)
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
} }
return nil return nil

View file

@ -128,10 +128,9 @@ func (ReplaceFilter) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (f *ReplaceFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (f *ReplaceFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume filter name
if d.NextArg() { if d.NextArg() {
f.Value = d.Val() f.Value = d.Val()
}
} }
return nil return nil
} }
@ -169,32 +168,31 @@ func (IPMaskFilter) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (m *IPMaskFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *IPMaskFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume filter name
for d.NextBlock(0) { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "ipv4": case "ipv4":
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
val, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("error parsing %s: %v", d.Val(), err)
}
m.IPv4MaskRaw = val
case "ipv6":
if !d.NextArg() {
return d.ArgErr()
}
val, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("error parsing %s: %v", d.Val(), err)
}
m.IPv6MaskRaw = val
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
val, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("error parsing %s: %v", d.Val(), err)
}
m.IPv4MaskRaw = val
case "ipv6":
if !d.NextArg() {
return d.ArgErr()
}
val, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("error parsing %s: %v", d.Val(), err)
}
m.IPv6MaskRaw = val
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
} }
return nil return nil
@ -328,45 +326,44 @@ func (QueryFilter) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (m *QueryFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *QueryFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume filter name
for d.NextBlock(0) { for d.NextBlock(0) {
qfa := queryFilterAction{} qfa := queryFilterAction{}
switch d.Val() { switch d.Val() {
case "replace": case "replace":
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
qfa.Type = replaceAction
qfa.Parameter = d.Val()
if !d.NextArg() {
return d.ArgErr()
}
qfa.Value = d.Val()
case "hash":
if !d.NextArg() {
return d.ArgErr()
}
qfa.Type = hashAction
qfa.Parameter = d.Val()
case "delete":
if !d.NextArg() {
return d.ArgErr()
}
qfa.Type = deleteAction
qfa.Parameter = d.Val()
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
m.Actions = append(m.Actions, qfa) qfa.Type = replaceAction
qfa.Parameter = d.Val()
if !d.NextArg() {
return d.ArgErr()
}
qfa.Value = d.Val()
case "hash":
if !d.NextArg() {
return d.ArgErr()
}
qfa.Type = hashAction
qfa.Parameter = d.Val()
case "delete":
if !d.NextArg() {
return d.ArgErr()
}
qfa.Type = deleteAction
qfa.Parameter = d.Val()
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
m.Actions = append(m.Actions, qfa)
} }
return nil return nil
} }
@ -460,45 +457,44 @@ func (CookieFilter) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (m *CookieFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *CookieFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume filter name
for d.NextBlock(0) { for d.NextBlock(0) {
cfa := cookieFilterAction{} cfa := cookieFilterAction{}
switch d.Val() { switch d.Val() {
case "replace": case "replace":
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
cfa.Type = replaceAction
cfa.Name = d.Val()
if !d.NextArg() {
return d.ArgErr()
}
cfa.Value = d.Val()
case "hash":
if !d.NextArg() {
return d.ArgErr()
}
cfa.Type = hashAction
cfa.Name = d.Val()
case "delete":
if !d.NextArg() {
return d.ArgErr()
}
cfa.Type = deleteAction
cfa.Name = d.Val()
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
m.Actions = append(m.Actions, cfa) cfa.Type = replaceAction
cfa.Name = d.Val()
if !d.NextArg() {
return d.ArgErr()
}
cfa.Value = d.Val()
case "hash":
if !d.NextArg() {
return d.ArgErr()
}
cfa.Type = hashAction
cfa.Name = d.Val()
case "delete":
if !d.NextArg() {
return d.ArgErr()
}
cfa.Type = deleteAction
cfa.Name = d.Val()
default:
return d.Errf("unrecognized subdirective %s", d.Val())
} }
m.Actions = append(m.Actions, cfa)
} }
return nil return nil
} }
@ -571,13 +567,12 @@ func (RegexpFilter) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (f *RegexpFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (f *RegexpFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume filter name
if d.NextArg() { if d.NextArg() {
f.RawRegexp = d.Val() f.RawRegexp = d.Val()
} }
if d.NextArg() { if d.NextArg() {
f.Value = d.Val() f.Value = d.Val()
}
} }
return nil return nil
} }
@ -625,10 +620,9 @@ func (RenameFilter) CaddyModule() caddy.ModuleInfo {
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (f *RenameFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (f *RenameFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume filter name
if d.NextArg() { if d.NextArg() {
f.Name = d.Val() f.Name = d.Val()
}
} }
return nil return nil
} }

View file

@ -117,35 +117,34 @@ func (nw NetWriter) OpenWriter() (io.WriteCloser, error) {
// soft_start // soft_start
// } // }
func (nw *NetWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (nw *NetWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume writer name
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
} }
nw.Address = d.Val() nw.Address = d.Val()
if d.NextArg() { if d.NextArg() {
return d.ArgErr() return d.ArgErr()
} }
for nesting := d.Nesting(); d.NextBlock(nesting); { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "dial_timeout": case "dial_timeout":
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
}
timeout, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("invalid duration: %s", d.Val())
}
if d.NextArg() {
return d.ArgErr()
}
nw.DialTimeout = caddy.Duration(timeout)
case "soft_start":
if d.NextArg() {
return d.ArgErr()
}
nw.SoftStart = true
} }
timeout, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("invalid duration: %s", d.Val())
}
if d.NextArg() {
return d.ArgErr()
}
nw.DialTimeout = caddy.Duration(timeout)
case "soft_start":
if d.NextArg() {
return d.ArgErr()
}
nw.SoftStart = true
} }
} }
return nil return nil

View file

@ -78,19 +78,18 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
// disable_openmetrics // disable_openmetrics
// } // }
func (m *Metrics) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m *Metrics) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { d.Next() // consume directive name
args := d.RemainingArgs() args := d.RemainingArgs()
if len(args) > 0 { if len(args) > 0 {
return d.ArgErr() return d.ArgErr()
} }
for d.NextBlock(0) { for d.NextBlock(0) {
switch d.Val() { switch d.Val() {
case "disable_openmetrics": case "disable_openmetrics":
m.DisableOpenMetrics = true m.DisableOpenMetrics = true
default: default:
return d.Errf("unrecognized subdirective %q", d.Val()) return d.Errf("unrecognized subdirective %q", d.Val())
}
} }
} }
return nil return nil