@marcel91200 / caddy Mirror · read-only / commits / aca4002f
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 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 +}