Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 12, 10:45 UTC
caddyfile: Pass blocks to `import` for snippets (#6130)
* a * a * a * a * a * a
a committed
Jun 14, 2024 at 12:27 UTC
aca4002fd8ed890f29f74bb7e8d629496f9a6e07
5 files changed
+337
-2
caddyconfig/caddyfile/parse.go
+65
-2
@@ -364,9 +364,45 @@ func (p *parser) doImport(nesting int) error {
364
// set up a replacer for non-variadic args replacement
365
repl := makeArgsReplacer(args)
366
367
+ // grab all the tokens (if it exists) from within a block that follows the import
368
+ var blockTokens []Token
369
+ for currentNesting := p.Nesting(); p.NextBlock(currentNesting); {
370
+ blockTokens = append(blockTokens, p.Token())
371
+ }
372
+ // initialize with size 1
373
+ blockMapping := make(map[string][]Token, 1)
374
+ if len(blockTokens) > 0 {
375
+ // use such tokens to create a new dispenser, and then use it to parse each block
376
+ bd := NewDispenser(blockTokens)
377
+ for bd.Next() {
378
+ // see if we can grab a key
379
+ var currentMappingKey string
380
+ if bd.Val() == "{" {
381
+ return p.Err("anonymous blocks are not supported")
382
+ }
383
+ currentMappingKey = bd.Val()
384
+ currentMappingTokens := []Token{}
385
+ // read all args until end of line / {
386
+ if bd.NextArg() {
387
+ currentMappingTokens = append(currentMappingTokens, bd.Token())
388
+ for bd.NextArg() {
389
+ currentMappingTokens = append(currentMappingTokens, bd.Token())
390
+ }
391
+ // TODO(elee1766): we don't enter another mapping here because it's annoying to extract the { and } properly.
392
+ // maybe someone can do that in the future
393
+ } else {
394
+ // attempt to enter a block and add tokens to the currentMappingTokens
395
+ for mappingNesting := bd.Nesting(); bd.NextBlock(mappingNesting); {
396
+ currentMappingTokens = append(currentMappingTokens, bd.Token())
397
+ }
398
+ }
399
+ blockMapping[currentMappingKey] = currentMappingTokens
400
+ }
401
+ }
402
+
403
// splice out the import directive and its arguments
404
// (2 tokens, plus the length of args)
369
- tokensBefore := p.tokens[:p.cursor-1-len(args)]
405
+ tokensBefore := p.tokens[:p.cursor-1-len(args)-len(blockTokens)]
406
tokensAfter := p.tokens[p.cursor+1:]
407
var importedTokens []Token
408
var nodes []string
@@ -495,6 +531,33 @@ func (p *parser) doImport(nesting int) error {
531
maybeSnippet = false
532
}
533
}
534
+ // if it is {block}, we substitute with all tokens in the block
535
+ // if it is {blocks.*}, we substitute with the tokens in the mapping for the *
536
+ var skip bool
537
+ var tokensToAdd []Token
538
+ switch {
539
+ case token.Text == "{block}":
540
+ tokensToAdd = blockTokens
541
+ case strings.HasPrefix(token.Text, "{blocks.") && strings.HasSuffix(token.Text, "}"):
542
+ // {blocks.foo.bar} will be extracted to key `foo.bar`
543
+ blockKey := strings.TrimPrefix(strings.TrimSuffix(token.Text, "}"), "{blocks.")
544
+ val, ok := blockMapping[blockKey]
545
+ if ok {
546
+ tokensToAdd = val
547
+ }
548
+ default:
549
+ skip = true
550
+ }
551
+ if !skip {
552
+ if len(tokensToAdd) == 0 {
553
+ // if there is no content in the snippet block, don't do any replacement
554
+ // this allows snippets which contained {block}/{block.*} before this change to continue functioning as normal
555
+ tokensCopy = append(tokensCopy, token)
556
+ } else {
557
+ tokensCopy = append(tokensCopy, tokensToAdd...)
558
+ }
559
+ continue
560
+ }
561
562
if maybeSnippet {
563
tokensCopy = append(tokensCopy, token)
@@ -516,7 +579,7 @@ func (p *parser) doImport(nesting int) error {
579
// splice the imported tokens in the place of the import statement
580
// and rewind cursor so Next() will land on first imported token
581
p.tokens = append(tokensBefore, append(tokensCopy, tokensAfter...)...)
519
- p.cursor -= len(args) + 1
582
+ p.cursor -= len(args) + len(blockTokens) + 1
583
584
return nil
585
}
caddytest/integration/caddyfile_adapt/import_block_snippet.caddyfiletest
new
+58
@@ -0,0 +1,58 @@
1
+(snippet) {
2
+ header {
3
+ {block}
4
+ }
5
+}
6
+
7
+example.com {
8
+ import snippet {
9
+ foo bar
10
+ }
11
+}
12
+----------
13
+{
14
+ "apps": {
15
+ "http": {
16
+ "servers": {
17
+ "srv0": {
18
+ "listen": [
19
+ ":443"
20
+ ],
21
+ "routes": [
22
+ {
23
+ "match": [
24
+ {
25
+ "host": [
26
+ "example.com"
27
+ ]
28
+ }
29
+ ],
30
+ "handle": [
31
+ {
32
+ "handler": "subroute",
33
+ "routes": [
34
+ {
35
+ "handle": [
36
+ {
37
+ "handler": "headers",
38
+ "response": {
39
+ "set": {
40
+ "Foo": [
41
+ "bar"
42
+ ]
43
+ }
44
+ }
45
+ }
46
+ ]
47
+ }
48
+ ]
49
+ }
50
+ ],
51
+ "terminal": true
52
+ }
53
+ ]
54
+ }
55
+ }
56
+ }
57
+ }
58
+}
caddytest/integration/caddyfile_adapt/import_block_snippet_args.caddyfiletest
new
+56
@@ -0,0 +1,56 @@
1
+(snippet) {
2
+ {block}
3
+}
4
+
5
+example.com {
6
+ import snippet {
7
+ header foo bar
8
+ }
9
+}
10
+----------
11
+{
12
+ "apps": {
13
+ "http": {
14
+ "servers": {
15
+ "srv0": {
16
+ "listen": [
17
+ ":443"
18
+ ],
19
+ "routes": [
20
+ {
21
+ "match": [
22
+ {
23
+ "host": [
24
+ "example.com"
25
+ ]
26
+ }
27
+ ],
28
+ "handle": [
29
+ {
30
+ "handler": "subroute",
31
+ "routes": [
32
+ {
33
+ "handle": [
34
+ {
35
+ "handler": "headers",
36
+ "response": {
37
+ "set": {
38
+ "Foo": [
39
+ "bar"
40
+ ]
41
+ }
42
+ }
43
+ }
44
+ ]
45
+ }
46
+ ]
47
+ }
48
+ ],
49
+ "terminal": true
50
+ }
51
+ ]
52
+ }
53
+ }
54
+ }
55
+ }
56
+}
caddytest/integration/caddyfile_adapt/import_blocks_snippet.caddyfiletest
new
+76
@@ -0,0 +1,76 @@
1
+(snippet) {
2
+ header {
3
+ {blocks.foo}
4
+ }
5
+ header {
6
+ {blocks.bar}
7
+ }
8
+}
9
+
10
+example.com {
11
+ import snippet {
12
+ foo {
13
+ foo a
14
+ }
15
+ bar {
16
+ bar b
17
+ }
18
+ }
19
+}
20
+----------
21
+{
22
+ "apps": {
23
+ "http": {
24
+ "servers": {
25
+ "srv0": {
26
+ "listen": [
27
+ ":443"
28
+ ],
29
+ "routes": [
30
+ {
31
+ "match": [
32
+ {
33
+ "host": [
34
+ "example.com"
35
+ ]
36
+ }
37
+ ],
38
+ "handle": [
39
+ {
40
+ "handler": "subroute",
41
+ "routes": [
42
+ {
43
+ "handle": [
44
+ {
45
+ "handler": "headers",
46
+ "response": {
47
+ "set": {
48
+ "Foo": [
49
+ "a"
50
+ ]
51
+ }
52
+ }
53
+ },
54
+ {
55
+ "handler": "headers",
56
+ "response": {
57
+ "set": {
58
+ "Bar": [
59
+ "b"
60
+ ]
61
+ }
62
+ }
63
+ }
64
+ ]
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ "terminal": true
70
+ }
71
+ ]
72
+ }
73
+ }
74
+ }
75
+ }
76
+}
caddytest/integration/caddyfile_adapt/import_blocks_snippet_nested.caddyfiletest
new
+82
@@ -0,0 +1,82 @@
1
+(snippet) {
2
+ header {
3
+ {blocks.bar}
4
+ }
5
+ import sub_snippet {
6
+ bar {
7
+ {blocks.foo}
8
+ }
9
+ }
10
+}
11
+(sub_snippet) {
12
+ header {
13
+ {blocks.bar}
14
+ }
15
+}
16
+example.com {
17
+ import snippet {
18
+ foo {
19
+ foo a
20
+ }
21
+ bar {
22
+ bar b
23
+ }
24
+ }
25
+}
26
+----------
27
+{
28
+ "apps": {
29
+ "http": {
30
+ "servers": {
31
+ "srv0": {
32
+ "listen": [
33
+ ":443"
34
+ ],
35
+ "routes": [
36
+ {
37
+ "match": [
38
+ {
39
+ "host": [
40
+ "example.com"
41
+ ]
42
+ }
43
+ ],
44
+ "handle": [
45
+ {
46
+ "handler": "subroute",
47
+ "routes": [
48
+ {
49
+ "handle": [
50
+ {
51
+ "handler": "headers",
52
+ "response": {
53
+ "set": {
54
+ "Bar": [
55
+ "b"
56
+ ]
57
+ }
58
+ }
59
+ },
60
+ {
61
+ "handler": "headers",
62
+ "response": {
63
+ "set": {
64
+ "Foo": [
65
+ "a"
66
+ ]
67
+ }
68
+ }
69
+ }
70
+ ]
71
+ }
72
+ ]
73
+ }
74
+ ],
75
+ "terminal": true
76
+ }
77
+ ]
78
+ }
79
+ }
80
+ }
81
+ }
82
+}