From cee4441cb1d485b38d728168a315cda5641d84fb Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Tue, 23 May 2023 05:36:55 +0800 Subject: [PATCH] caddyfile: Do not replace import tokens if they are part of a snippet (#5539) * fix variadic placeholder in imported file which also imports * fix tests. * skip replacing args when imported token may be part of a snippet --- caddyconfig/caddyfile/parse.go | 50 +++++++++++++++++-- caddyconfig/httpcaddyfile/builtins_test.go | 21 ++++++++ .../testdata/import_variadic.txt | 9 ++++ .../testdata/import_variadic_snippet.txt | 9 ++++ .../testdata/import_variadic_with_import.txt | 15 ++++++ 5 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 caddyconfig/httpcaddyfile/testdata/import_variadic.txt create mode 100644 caddyconfig/httpcaddyfile/testdata/import_variadic_snippet.txt create mode 100644 caddyconfig/httpcaddyfile/testdata/import_variadic_with_import.txt diff --git a/caddyconfig/caddyfile/parse.go b/caddyconfig/caddyfile/parse.go index 64d10625..5afdc217 100644 --- a/caddyconfig/caddyfile/parse.go +++ b/caddyconfig/caddyfile/parse.go @@ -214,7 +214,7 @@ func (p *parser) addresses() error { // special case: import directive replaces tokens during parse-time if tkn == "import" && p.isNewLine() { - err := p.doImport() + err := p.doImport(0) if err != nil { return err } @@ -314,7 +314,7 @@ func (p *parser) directives() error { // special case: import directive replaces tokens during parse-time if p.Val() == "import" { - err := p.doImport() + err := p.doImport(1) if err != nil { return err } @@ -340,7 +340,7 @@ func (p *parser) directives() error { // is on the token before where the import directive was. In // other words, call Next() to access the first token that was // imported. -func (p *parser) doImport() error { +func (p *parser) doImport(nesting int) error { // syntax checks if !p.NextArg() { return p.ArgErr() @@ -443,10 +443,16 @@ func (p *parser) doImport() error { // copy the tokens so we don't overwrite p.definedSnippets tokensCopy := make([]Token, 0, len(importedTokens)) + var ( + maybeSnippet bool + maybeSnippetId bool + index int + ) + // run the argument replacer on the tokens // golang for range slice return a copy of value // similarly, append also copy value - for _, token := range importedTokens { + for i, token := range importedTokens { // set the token's file to refer to import directive line number and snippet name if token.snippetName != "" { token.updateFile(fmt.Sprintf("%s:%d (import %s)", token.File, p.Line(), token.snippetName)) @@ -454,6 +460,40 @@ func (p *parser) doImport() error { token.updateFile(fmt.Sprintf("%s:%d (import)", token.File, p.Line())) } + // naive way of determine snippets, as snippets definition can only follow name + block + // format, won't check for nesting correctness or any other error, that's what parser does. + if !maybeSnippet && nesting == 0 { + // first of the line + if i == 0 || importedTokens[i-1].originalFile() != token.originalFile() || importedTokens[i-1].Line+importedTokens[i-1].NumLineBreaks() < token.Line { + index = 0 + } else { + index++ + } + + if index == 0 && len(token.Text) >= 3 && strings.HasPrefix(token.Text, "(") && strings.HasSuffix(token.Text, ")") { + maybeSnippetId = true + } + } + + switch token.Text { + case "{": + nesting++ + if index == 1 && maybeSnippetId && nesting == 1 { + maybeSnippet = true + maybeSnippetId = false + } + case "}": + nesting-- + if nesting == 0 && maybeSnippet { + maybeSnippet = false + } + } + + if maybeSnippet { + tokensCopy = append(tokensCopy, token) + continue + } + foundVariadic, startIndex, endIndex := parseVariadic(token, len(args)) if foundVariadic { for _, arg := range args[startIndex:endIndex] { @@ -553,7 +593,7 @@ func (p *parser) directive() error { } else if p.Val() == "}" && p.nesting == 0 { return p.Err("Unexpected '}' because no matching opening brace") } else if p.Val() == "import" && p.isNewLine() { - if err := p.doImport(); err != nil { + if err := p.doImport(1); err != nil { return err } p.cursor-- // cursor is advanced when we continue, so roll back one more diff --git a/caddyconfig/httpcaddyfile/builtins_test.go b/caddyconfig/httpcaddyfile/builtins_test.go index 6ae4b311..34b41247 100644 --- a/caddyconfig/httpcaddyfile/builtins_test.go +++ b/caddyconfig/httpcaddyfile/builtins_test.go @@ -243,6 +243,27 @@ func TestImportErrorLine(t *testing.T) { return err != nil && strings.Contains(err.Error(), "Caddyfile:5 (import t1):2") }, }, + { + input: ` + import testdata/import_variadic_snippet.txt + :8080 { + import t1 true + }`, + errorFunc: func(err error) bool { + return err == nil + }, + }, + { + input: ` + import testdata/import_variadic_with_import.txt + :8080 { + import t1 true + import t2 true + }`, + errorFunc: func(err error) bool { + return err == nil + }, + }, } { adapter := caddyfile.Adapter{ ServerType: ServerType{}, diff --git a/caddyconfig/httpcaddyfile/testdata/import_variadic.txt b/caddyconfig/httpcaddyfile/testdata/import_variadic.txt new file mode 100644 index 00000000..f1e50e01 --- /dev/null +++ b/caddyconfig/httpcaddyfile/testdata/import_variadic.txt @@ -0,0 +1,9 @@ +(t2) { + respond 200 { + body {args[:]} + } +} + +:8082 { + import t2 false +} \ No newline at end of file diff --git a/caddyconfig/httpcaddyfile/testdata/import_variadic_snippet.txt b/caddyconfig/httpcaddyfile/testdata/import_variadic_snippet.txt new file mode 100644 index 00000000..a02fcf90 --- /dev/null +++ b/caddyconfig/httpcaddyfile/testdata/import_variadic_snippet.txt @@ -0,0 +1,9 @@ +(t1) { + respond 200 { + body {args[:]} + } +} + +:8081 { + import t1 false +} \ No newline at end of file diff --git a/caddyconfig/httpcaddyfile/testdata/import_variadic_with_import.txt b/caddyconfig/httpcaddyfile/testdata/import_variadic_with_import.txt new file mode 100644 index 00000000..ab1b32d9 --- /dev/null +++ b/caddyconfig/httpcaddyfile/testdata/import_variadic_with_import.txt @@ -0,0 +1,15 @@ +(t1) { + respond 200 { + body {args[:]} + } +} + +:8081 { + import t1 false +} + +import import_variadic.txt + +:8083 { + import t2 true +} \ No newline at end of file