@marcel91200 / caddy Mirror · read-only / commits / fdf2a77f
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

caddyfile: Add args on imports (#3423)

* caddyfile: Add support for args on imports * caddyfile: Add more import args tests

Francis Lavoie committed Jun 1, 2020 at 12:43 UTC fdf2a77feb53cb9dd6de772f811e96a5f6b2d2c4
7 files changed +168 -8
caddyconfig/caddyfile/parse.go
+27 -6
@@ -20,7 +20,10 @@ import (
20 "log"
21 "os"
22 "path/filepath"
23 + "strconv"
24 "strings"
25 +
26 + "github.com/caddyserver/caddy/v2"
27 )
28
29 // Parse parses the input just enough to group tokens, in
@@ -292,11 +295,19 @@ func (p *parser) doImport() error {
295 if importPattern == "" {
296 return p.Err("Import requires a non-empty filepath")
297 }
295 - if p.NextArg() {
296 - return p.Err("Import takes only one argument (glob pattern or file)")
298 +
299 + // grab remaining args as placeholder replacements
300 + args := p.RemainingArgs()
301 +
302 + // add args to the replacer
303 + repl := caddy.NewReplacer()
304 + for index, arg := range args {
305 + repl.Set("args."+strconv.Itoa(index), arg)
306 }
298 - // splice out the import directive and its argument (2 tokens total)
299 - tokensBefore := p.tokens[:p.cursor-1]
307 +
308 + // splice out the import directive and its arguments
309 + // (2 tokens, plus the length of args)
310 + tokensBefore := p.tokens[:p.cursor-1-len(args)]
311 tokensAfter := p.tokens[p.cursor+1:]
312 var importedTokens []Token
313
@@ -348,10 +359,20 @@ func (p *parser) doImport() error {
359 }
360 }
361
362 + // copy the tokens so we don't overwrite p.definedSnippets
363 + tokensCopy := make([]Token, len(importedTokens))
364 + copy(tokensCopy, importedTokens)
365 +
366 + // run the argument replacer on the tokens
367 + for index, token := range tokensCopy {
368 + token.Text = repl.ReplaceKnown(token.Text, "")
369 + tokensCopy[index] = token
370 + }
371 +
372 // splice the imported tokens in the place of the import statement
373 // and rewind cursor so Next() will land on first imported token
353 - p.tokens = append(tokensBefore, append(importedTokens, tokensAfter...)...)
354 - p.cursor--
374 + p.tokens = append(tokensBefore, append(tokensCopy, tokensAfter...)...)
375 + p.cursor -= len(args) + 1
376
377 return nil
378 }
caddyconfig/caddyfile/parse_test.go
+6 -2
@@ -182,14 +182,17 @@ func TestParseOneAndImport(t *testing.T) {
182 "host1",
183 }, []int{1, 2}},
184
185 - {`import testdata/import_test1.txt testdata/import_test2.txt`, true, []string{}, []int{}},
186 -
185 {`import testdata/not_found.txt`, true, []string{}, []int{}},
186
187 {`""`, false, []string{}, []int{}},
188
189 {``, false, []string{}, []int{}},
190
191 + // import with args
192 + {`import testdata/import_args0.txt a`, false, []string{"a"}, []int{}},
193 + {`import testdata/import_args1.txt a b`, false, []string{"a", "b"}, []int{}},
194 + {`import testdata/import_args*.txt a b`, false, []string{"a"}, []int{2}},
195 +
196 // test cases found by fuzzing!
197 {`import }{$"`, true, []string{}, []int{}},
198 {`import /*/*.txt`, true, []string{}, []int{}},
@@ -210,6 +213,7 @@ func TestParseOneAndImport(t *testing.T) {
213 t.Errorf("Test %d: Expected no error, but got: %v", i, err)
214 }
215
216 + // t.Logf("%+v\n", result)
217 if len(result.Keys) != len(test.keys) {
218 t.Errorf("Test %d: Expected %d keys, got %d",
219 i, len(test.keys), len(result.Keys))
caddyconfig/caddyfile/testdata/import_args0.txt new
+1
@@ -0,0 +1 @@
1 +{args.0}
\ No newline at end of file
caddyconfig/caddyfile/testdata/import_args1.txt new
+1
@@ -0,0 +1 @@
1 +{args.0} {args.1}
\ No newline at end of file
caddytest/integration/caddyfile_adapt/import_args_file.txt new
+49
@@ -0,0 +1,49 @@
1 +example.com
2 +
3 +import testdata/import_respond.txt Groot Rocket
4 +import testdata/import_respond.txt you "the confused man"
5 +----------
6 +{
7 + "apps": {
8 + "http": {
9 + "servers": {
10 + "srv0": {
11 + "listen": [
12 + ":443"
13 + ],
14 + "routes": [
15 + {
16 + "match": [
17 + {
18 + "host": [
19 + "example.com"
20 + ]
21 + }
22 + ],
23 + "handle": [
24 + {
25 + "handler": "subroute",
26 + "routes": [
27 + {
28 + "handle": [
29 + {
30 + "body": "'I am Groot', hears Rocket",
31 + "handler": "static_response"
32 + },
33 + {
34 + "body": "'I am you', hears the confused man",
35 + "handler": "static_response"
36 + }
37 + ]
38 + }
39 + ]
40 + }
41 + ],
42 + "terminal": true
43 + }
44 + ]
45 + }
46 + }
47 + }
48 + }
49 +}
\ No newline at end of file
caddytest/integration/caddyfile_adapt/import_args_snippet.txt new
+83
@@ -0,0 +1,83 @@
1 +(logging) {
2 + log {
3 + output file /var/log/caddy/{args.0}.access.log
4 + }
5 +}
6 +
7 +a.example.com {
8 + import logging a.example.com
9 +}
10 +
11 +b.example.com {
12 + import logging b.example.com
13 +}
14 +----------
15 +{
16 + "logging": {
17 + "logs": {
18 + "default": {
19 + "exclude": [
20 + "http.log.access.log0",
21 + "http.log.access.log1"
22 + ]
23 + },
24 + "log0": {
25 + "writer": {
26 + "filename": "/var/log/caddy/a.example.com.access.log",
27 + "output": "file"
28 + },
29 + "include": [
30 + "http.log.access.log0"
31 + ]
32 + },
33 + "log1": {
34 + "writer": {
35 + "filename": "/var/log/caddy/b.example.com.access.log",
36 + "output": "file"
37 + },
38 + "include": [
39 + "http.log.access.log1"
40 + ]
41 + }
42 + }
43 + },
44 + "apps": {
45 + "http": {
46 + "servers": {
47 + "srv0": {
48 + "listen": [
49 + ":443"
50 + ],
51 + "routes": [
52 + {
53 + "match": [
54 + {
55 + "host": [
56 + "a.example.com"
57 + ]
58 + }
59 + ],
60 + "terminal": true
61 + },
62 + {
63 + "match": [
64 + {
65 + "host": [
66 + "b.example.com"
67 + ]
68 + }
69 + ],
70 + "terminal": true
71 + }
72 + ],
73 + "logs": {
74 + "logger_names": {
75 + "a.example.com": "log0",
76 + "b.example.com": "log1"
77 + }
78 + }
79 + }
80 + }
81 + }
82 + }
83 +}
\ No newline at end of file
caddytest/integration/testdata/import_respond.txt new
+1
@@ -0,0 +1 @@
1 +respond "'I am {args.0}', hears {args.1}"
\ No newline at end of file