From 8bc05e598da0068358a2876c45f92f2c77455341 Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Fri, 17 Feb 2023 08:08:36 +0800 Subject: caddyfile: Implement variadics for import args placeholders (#5249) * implement variadic placeholders imported snippets reflect actual lines in file * add import directive line number for imported snippets add tests for parsing * add realfile field to help debug import cycle detection. * use file field to reflect import chain * Switch syntax, deprecate old syntax, refactoring - Moved the import args handling to a separate file - Using {args[0:1]} syntax now - Deprecate {args.*} syntax - Use a replacer map for better control over the parsing - Add plenty of warnings when invalid placeholders are detected - Renaming variables, cleanup comments for readability - More tests to cover edgecases I could think of - Minor cleanup to snippet tracking in tokens, drop a redundant boolean field in tokens --------- Co-authored-by: Francis Lavoie --- caddyconfig/caddyfile/importargs.go | 142 ++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 caddyconfig/caddyfile/importargs.go (limited to 'caddyconfig/caddyfile/importargs.go') diff --git a/caddyconfig/caddyfile/importargs.go b/caddyconfig/caddyfile/importargs.go new file mode 100644 index 0000000..53725a1 --- /dev/null +++ b/caddyconfig/caddyfile/importargs.go @@ -0,0 +1,142 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package caddyfile + +import ( + "regexp" + "strconv" + "strings" + + "github.com/caddyserver/caddy/v2" + "go.uber.org/zap" +) + +// parseVariadic determines if the token is a variadic placeholder, +// and if so, determines the index range (start/end) of args to use. +// Returns a boolean signaling whether a variadic placeholder was found, +// and the start and end indices. +func parseVariadic(token Token, argCount int) (bool, int, int) { + if !strings.HasPrefix(token.Text, "{args[") { + return false, 0, 0 + } + if !strings.HasSuffix(token.Text, "]}") { + return false, 0, 0 + } + + argRange := strings.TrimSuffix(strings.TrimPrefix(token.Text, "{args["), "]}") + if argRange == "" { + caddy.Log().Named("caddyfile").Warn( + "Placeholder "+token.Text+" cannot have an empty index", + zap.String("file", token.File+":"+strconv.Itoa(token.Line))) + return false, 0, 0 + } + + start, end, found := strings.Cut(argRange, ":") + + // If no ":" delimiter is found, this is not a variadic. + // The replacer will pick this up. + if !found { + return false, 0, 0 + } + + var ( + startIndex = 0 + endIndex = argCount + err error + ) + if start != "" { + startIndex, err = strconv.Atoi(start) + if err != nil { + caddy.Log().Named("caddyfile").Warn( + "Variadic placeholder "+token.Text+" has an invalid start index", + zap.String("file", token.File+":"+strconv.Itoa(token.Line))) + return false, 0, 0 + } + } + if end != "" { + endIndex, err = strconv.Atoi(end) + if err != nil { + caddy.Log().Named("caddyfile").Warn( + "Variadic placeholder "+token.Text+" has an invalid end index", + zap.String("file", token.File+":"+strconv.Itoa(token.Line))) + return false, 0, 0 + } + } + + // bound check + if startIndex < 0 || startIndex > endIndex || endIndex > argCount { + caddy.Log().Named("caddyfile").Warn( + "Variadic placeholder "+token.Text+" indices are out of bounds, only "+strconv.Itoa(argCount)+" argument(s) exist", + zap.String("file", token.File+":"+strconv.Itoa(token.Line))) + return false, 0, 0 + } + return true, startIndex, endIndex +} + +// makeArgsReplacer prepares a Replacer which can replace +// non-variadic args placeholders in imported tokens. +func makeArgsReplacer(args []string) *caddy.Replacer { + repl := caddy.NewEmptyReplacer() + repl.Map(func(key string) (any, bool) { + // TODO: Remove the deprecated {args.*} placeholder + // support at some point in the future + if matches := argsRegexpIndexDeprecated.FindStringSubmatch(key); len(matches) > 0 { + value, err := strconv.Atoi(matches[1]) + if err != nil { + caddy.Log().Named("caddyfile").Warn( + "Placeholder {args." + matches[1] + "} has an invalid index") + return nil, false + } + if value >= len(args) { + caddy.Log().Named("caddyfile").Warn( + "Placeholder {args." + matches[1] + "} index is out of bounds, only " + strconv.Itoa(len(args)) + " argument(s) exist") + return nil, false + } + caddy.Log().Named("caddyfile").Warn( + "Placeholder {args." + matches[1] + "} deprecated, use {args[" + matches[1] + "]} instead") + return args[value], true + } + + // Handle args[*] form + if matches := argsRegexpIndex.FindStringSubmatch(key); len(matches) > 0 { + if strings.Contains(matches[1], ":") { + caddy.Log().Named("caddyfile").Warn( + "Variadic placeholder {args[" + matches[1] + "]} must be a token on its own") + return nil, false + } + value, err := strconv.Atoi(matches[1]) + if err != nil { + caddy.Log().Named("caddyfile").Warn( + "Placeholder {args[" + matches[1] + "]} has an invalid index") + return nil, false + } + if value >= len(args) { + caddy.Log().Named("caddyfile").Warn( + "Placeholder {args[" + matches[1] + "]} index is out of bounds, only " + strconv.Itoa(len(args)) + " argument(s) exist") + return nil, false + } + return args[value], true + } + + // Not an args placeholder, ignore + return nil, false + }) + return repl +} + +var ( + argsRegexpIndexDeprecated = regexp.MustCompile(`args\.(.+)`) + argsRegexpIndex = regexp.MustCompile(`args\[(.+)]`) +) -- cgit v1.2.3 From 9cde71552576dcc724eff38dee42879c927b72ec Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Fri, 26 May 2023 03:05:00 +0800 Subject: caddyfile: Track import name instead of modifying filename (#5540) * Merge branch 'master' into import_file_stack * remove space in log key --- caddyconfig/caddyfile/importargs.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'caddyconfig/caddyfile/importargs.go') diff --git a/caddyconfig/caddyfile/importargs.go b/caddyconfig/caddyfile/importargs.go index 53725a1..c6dcd85 100644 --- a/caddyconfig/caddyfile/importargs.go +++ b/caddyconfig/caddyfile/importargs.go @@ -39,7 +39,7 @@ func parseVariadic(token Token, argCount int) (bool, int, int) { if argRange == "" { caddy.Log().Named("caddyfile").Warn( "Placeholder "+token.Text+" cannot have an empty index", - zap.String("file", token.File+":"+strconv.Itoa(token.Line))) + zap.String("file", token.File+":"+strconv.Itoa(token.Line)), zap.Strings("import_chain", token.imports)) return false, 0, 0 } @@ -61,7 +61,7 @@ func parseVariadic(token Token, argCount int) (bool, int, int) { if err != nil { caddy.Log().Named("caddyfile").Warn( "Variadic placeholder "+token.Text+" has an invalid start index", - zap.String("file", token.File+":"+strconv.Itoa(token.Line))) + zap.String("file", token.File+":"+strconv.Itoa(token.Line)), zap.Strings("import_chain", token.imports)) return false, 0, 0 } } @@ -70,7 +70,7 @@ func parseVariadic(token Token, argCount int) (bool, int, int) { if err != nil { caddy.Log().Named("caddyfile").Warn( "Variadic placeholder "+token.Text+" has an invalid end index", - zap.String("file", token.File+":"+strconv.Itoa(token.Line))) + zap.String("file", token.File+":"+strconv.Itoa(token.Line)), zap.Strings("import_chain", token.imports)) return false, 0, 0 } } @@ -79,7 +79,7 @@ func parseVariadic(token Token, argCount int) (bool, int, int) { if startIndex < 0 || startIndex > endIndex || endIndex > argCount { caddy.Log().Named("caddyfile").Warn( "Variadic placeholder "+token.Text+" indices are out of bounds, only "+strconv.Itoa(argCount)+" argument(s) exist", - zap.String("file", token.File+":"+strconv.Itoa(token.Line))) + zap.String("file", token.File+":"+strconv.Itoa(token.Line)), zap.Strings("import_chain", token.imports)) return false, 0, 0 } return true, startIndex, endIndex -- cgit v1.2.3 From 9f34383c02f1691e54280285a6499893fcbbb4c7 Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Sat, 5 Aug 2023 00:44:38 +0800 Subject: caddyfile: check that matched key is not a substring of the replacement key (#5685) --- caddyconfig/caddyfile/importargs.go | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'caddyconfig/caddyfile/importargs.go') diff --git a/caddyconfig/caddyfile/importargs.go b/caddyconfig/caddyfile/importargs.go index c6dcd85..54d648e 100644 --- a/caddyconfig/caddyfile/importargs.go +++ b/caddyconfig/caddyfile/importargs.go @@ -93,6 +93,11 @@ func makeArgsReplacer(args []string) *caddy.Replacer { // TODO: Remove the deprecated {args.*} placeholder // support at some point in the future if matches := argsRegexpIndexDeprecated.FindStringSubmatch(key); len(matches) > 0 { + // What's matched may be a substring of the key + if matches[0] != key { + return nil, false + } + value, err := strconv.Atoi(matches[1]) if err != nil { caddy.Log().Named("caddyfile").Warn( @@ -111,6 +116,11 @@ func makeArgsReplacer(args []string) *caddy.Replacer { // Handle args[*] form if matches := argsRegexpIndex.FindStringSubmatch(key); len(matches) > 0 { + // What's matched may be a substring of the key + if matches[0] != key { + return nil, false + } + if strings.Contains(matches[1], ":") { caddy.Log().Named("caddyfile").Warn( "Variadic placeholder {args[" + matches[1] + "]} must be a token on its own") -- cgit v1.2.3 From d6f86cccf5fa5b4eb30141da390cf2439746c5da Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Mon, 14 Aug 2023 23:41:15 +0800 Subject: ci: use gci linter (#5708) * use gofmput to format code * use gci to format imports * reconfigure gci * linter autofixes * rearrange imports a little * export GOOS=windows golangci-lint run ./... --fix --- caddyconfig/caddyfile/importargs.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'caddyconfig/caddyfile/importargs.go') diff --git a/caddyconfig/caddyfile/importargs.go b/caddyconfig/caddyfile/importargs.go index 54d648e..2e21a36 100644 --- a/caddyconfig/caddyfile/importargs.go +++ b/caddyconfig/caddyfile/importargs.go @@ -19,8 +19,9 @@ import ( "strconv" "strings" - "github.com/caddyserver/caddy/v2" "go.uber.org/zap" + + "github.com/caddyserver/caddy/v2" ) // parseVariadic determines if the token is a variadic placeholder, -- cgit v1.2.3