Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 08:30 UTC
caddyconfig: Register nested named-route invokes transitively (#7898)
SillyZir committed
Jul 30, 2026 at 06:55 UTC
54a6b8bc9e22422966f87f61b85a54e7c518122c
3 files changed
+151
-4
caddyconfig/httpcaddyfile/httptype.go
+27
-4
@@ -464,6 +464,7 @@ func (ServerType) extractNamedRoutes(
464
replacer ShorthandReplacer,
465
) ([]serverBlock, error) {
466
namedRoutes := map[string]*caddyhttp.Route{}
467
+ namedRouteInvokes := map[string][]string{}
468
469
gc := counter{new(int)}
470
state := make(map[string]any)
@@ -528,8 +529,19 @@ func (ServerType) extractNamedRoutes(
529
return nil, fmt.Errorf("cannot have duplicate named_routes: %s", key)
530
}
531
namedRoutes[key] = &route
532
+
533
+ // remember which named routes this named route invokes,
534
+ // so they can also be attached to any server that invokes
535
+ // this one
536
+ if state[namedRouteKey] != nil {
537
+ for name := range state[namedRouteKey].(map[string]struct{}) {
538
+ namedRouteInvokes[key] = append(namedRouteInvokes[key], name)
539
+ }
540
+ state[namedRouteKey] = nil
541
+ }
542
}
543
options["named_routes"] = namedRoutes
544
+ options["named_route_invokes"] = namedRouteInvokes
545
546
return filtered, nil
547
}
@@ -721,6 +733,7 @@ func (st *ServerType) serversFromPairings(
733
734
// add named routes to the server if 'invoke' was used inside of it
735
configuredNamedRoutes := options["named_routes"].(map[string]*caddyhttp.Route)
736
+ namedRouteInvokes := options["named_route_invokes"].(map[string][]string)
737
for _, sblock := range p.serverBlocks {
738
if len(sblock.pile[namedRouteKey]) == 0 {
739
continue
@@ -729,11 +742,21 @@ func (st *ServerType) serversFromPairings(
742
if srv.NamedRoutes == nil {
743
srv.NamedRoutes = map[string]*caddyhttp.Route{}
744
}
732
- name := value.Value.(string)
733
- if configuredNamedRoutes[name] == nil {
734
- return nil, fmt.Errorf("cannot invoke named route '%s', which was not defined", name)
745
+ // named routes may invoke other named routes, so
746
+ // resolve the set of invoked names transitively
747
+ names := []string{value.Value.(string)}
748
+ for len(names) > 0 {
749
+ name := names[0]
750
+ names = names[1:]
751
+ if _, ok := srv.NamedRoutes[name]; ok {
752
+ continue
753
+ }
754
+ if configuredNamedRoutes[name] == nil {
755
+ return nil, fmt.Errorf("cannot invoke named route '%s', which was not defined", name)
756
+ }
757
+ srv.NamedRoutes[name] = configuredNamedRoutes[name]
758
+ names = append(names, namedRouteInvokes[name]...)
759
}
736
- srv.NamedRoutes[name] = configuredNamedRoutes[name]
760
}
761
}
762
caddytest/integration/caddyfile_adapt/invoke_nested_named_routes.caddyfiletest
new
+114
@@ -0,0 +1,114 @@
1
+&(a) {
2
+ invoke b
3
+ respond "a"
4
+}
5
+
6
+&(b) {
7
+ invoke c
8
+ respond "b"
9
+}
10
+
11
+&(c) {
12
+ respond "c"
13
+}
14
+
15
+&(loop1) {
16
+ invoke loop2
17
+ respond "loop1"
18
+}
19
+
20
+&(loop2) {
21
+ invoke loop1
22
+ respond "loop2"
23
+}
24
+
25
+:8080 {
26
+ invoke a
27
+}
28
+
29
+:8081 {
30
+ invoke loop1
31
+}
32
+----------
33
+{
34
+ "apps": {
35
+ "http": {
36
+ "servers": {
37
+ "srv0": {
38
+ "listen": [
39
+ ":8080"
40
+ ],
41
+ "routes": [
42
+ {
43
+ "handle": [
44
+ {
45
+ "handler": "invoke",
46
+ "name": "a"
47
+ }
48
+ ]
49
+ }
50
+ ],
51
+ "named_routes": {
52
+ "a": {
53
+ "handle": [
54
+ {
55
+ "handler": "invoke",
56
+ "name": "b"
57
+ }
58
+ ]
59
+ },
60
+ "b": {
61
+ "handle": [
62
+ {
63
+ "handler": "invoke",
64
+ "name": "c"
65
+ }
66
+ ]
67
+ },
68
+ "c": {
69
+ "handle": [
70
+ {
71
+ "body": "c",
72
+ "handler": "static_response"
73
+ }
74
+ ]
75
+ }
76
+ }
77
+ },
78
+ "srv1": {
79
+ "listen": [
80
+ ":8081"
81
+ ],
82
+ "routes": [
83
+ {
84
+ "handle": [
85
+ {
86
+ "handler": "invoke",
87
+ "name": "loop1"
88
+ }
89
+ ]
90
+ }
91
+ ],
92
+ "named_routes": {
93
+ "loop1": {
94
+ "handle": [
95
+ {
96
+ "handler": "invoke",
97
+ "name": "loop2"
98
+ }
99
+ ]
100
+ },
101
+ "loop2": {
102
+ "handle": [
103
+ {
104
+ "handler": "invoke",
105
+ "name": "loop1"
106
+ }
107
+ ]
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+}
caddytest/integration/caddyfile_adapt/invoke_nested_undefined_named_route.caddyfiletest
new
+10
@@ -0,0 +1,10 @@
1
+&(outer) {
2
+ invoke missing
3
+ respond "outer"
4
+}
5
+
6
+example.com {
7
+ invoke outer
8
+}
9
+----------
10
+cannot invoke named route 'missing', which was not defined