@marcel91200 / caddy Mirror · read-only / commits / 54a6b8bc
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 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