caddyconfig: Register nested named-route invokes transitively (#7898)

pull/7903/head^2
SillyZir 2026-07-30 06:55:49 -04:00 committed by GitHub
parent 3be8dabc89
commit 54a6b8bc9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 151 additions and 4 deletions

View File

@ -464,6 +464,7 @@ func (ServerType) extractNamedRoutes(
replacer ShorthandReplacer,
) ([]serverBlock, error) {
namedRoutes := map[string]*caddyhttp.Route{}
namedRouteInvokes := map[string][]string{}
gc := counter{new(int)}
state := make(map[string]any)
@ -528,8 +529,19 @@ func (ServerType) extractNamedRoutes(
return nil, fmt.Errorf("cannot have duplicate named_routes: %s", key)
}
namedRoutes[key] = &route
// remember which named routes this named route invokes,
// so they can also be attached to any server that invokes
// this one
if state[namedRouteKey] != nil {
for name := range state[namedRouteKey].(map[string]struct{}) {
namedRouteInvokes[key] = append(namedRouteInvokes[key], name)
}
state[namedRouteKey] = nil
}
}
options["named_routes"] = namedRoutes
options["named_route_invokes"] = namedRouteInvokes
return filtered, nil
}
@ -721,6 +733,7 @@ func (st *ServerType) serversFromPairings(
// add named routes to the server if 'invoke' was used inside of it
configuredNamedRoutes := options["named_routes"].(map[string]*caddyhttp.Route)
namedRouteInvokes := options["named_route_invokes"].(map[string][]string)
for _, sblock := range p.serverBlocks {
if len(sblock.pile[namedRouteKey]) == 0 {
continue
@ -729,11 +742,21 @@ func (st *ServerType) serversFromPairings(
if srv.NamedRoutes == nil {
srv.NamedRoutes = map[string]*caddyhttp.Route{}
}
name := value.Value.(string)
if configuredNamedRoutes[name] == nil {
return nil, fmt.Errorf("cannot invoke named route '%s', which was not defined", name)
// named routes may invoke other named routes, so
// resolve the set of invoked names transitively
names := []string{value.Value.(string)}
for len(names) > 0 {
name := names[0]
names = names[1:]
if _, ok := srv.NamedRoutes[name]; ok {
continue
}
if configuredNamedRoutes[name] == nil {
return nil, fmt.Errorf("cannot invoke named route '%s', which was not defined", name)
}
srv.NamedRoutes[name] = configuredNamedRoutes[name]
names = append(names, namedRouteInvokes[name]...)
}
srv.NamedRoutes[name] = configuredNamedRoutes[name]
}
}

View File

@ -0,0 +1,114 @@
&(a) {
invoke b
respond "a"
}
&(b) {
invoke c
respond "b"
}
&(c) {
respond "c"
}
&(loop1) {
invoke loop2
respond "loop1"
}
&(loop2) {
invoke loop1
respond "loop2"
}
:8080 {
invoke a
}
:8081 {
invoke loop1
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8080"
],
"routes": [
{
"handle": [
{
"handler": "invoke",
"name": "a"
}
]
}
],
"named_routes": {
"a": {
"handle": [
{
"handler": "invoke",
"name": "b"
}
]
},
"b": {
"handle": [
{
"handler": "invoke",
"name": "c"
}
]
},
"c": {
"handle": [
{
"body": "c",
"handler": "static_response"
}
]
}
}
},
"srv1": {
"listen": [
":8081"
],
"routes": [
{
"handle": [
{
"handler": "invoke",
"name": "loop1"
}
]
}
],
"named_routes": {
"loop1": {
"handle": [
{
"handler": "invoke",
"name": "loop2"
}
]
},
"loop2": {
"handle": [
{
"handler": "invoke",
"name": "loop1"
}
]
}
}
}
}
}
}
}

View File

@ -0,0 +1,10 @@
&(outer) {
invoke missing
respond "outer"
}
example.com {
invoke outer
}
----------
cannot invoke named route 'missing', which was not defined