@@ -52,8 +52,10 @@ type ServerType struct {
52
52
}
53
53
54
54
// Setup makes a config from the tokens.
55
- func (st ServerType ) Setup (inputServerBlocks []caddyfile.ServerBlock ,
56
- options map [string ]any ) (* caddy.Config , []caddyconfig.Warning , error ) {
55
+ func (st ServerType ) Setup (
56
+ inputServerBlocks []caddyfile.ServerBlock ,
57
+ options map [string ]any ,
58
+ ) (* caddy.Config , []caddyconfig.Warning , error ) {
57
59
var warnings []caddyconfig.Warning
58
60
gc := counter {new (int )}
59
61
state := make (map [string ]any )
@@ -79,6 +81,11 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
79
81
return nil , warnings , err
80
82
}
81
83
84
+ originalServerBlocks , err = st .extractNamedRoutes (originalServerBlocks , options , & warnings )
85
+ if err != nil {
86
+ return nil , warnings , err
87
+ }
88
+
82
89
// replace shorthand placeholders (which are convenient
83
90
// when writing a Caddyfile) with their actual placeholder
84
91
// identifiers or variable names
@@ -172,6 +179,18 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
172
179
result .directive = dir
173
180
sb .pile [result .Class ] = append (sb .pile [result .Class ], result )
174
181
}
182
+
183
+ // specially handle named routes that were pulled out from
184
+ // the invoke directive, which could be nested anywhere within
185
+ // some subroutes in this directive; we add them to the pile
186
+ // for this server block
187
+ if state [namedRouteKey ] != nil {
188
+ for name := range state [namedRouteKey ].(map [string ]struct {}) {
189
+ result := ConfigValue {Class : namedRouteKey , Value : name }
190
+ sb .pile [result .Class ] = append (sb .pile [result .Class ], result )
191
+ }
192
+ state [namedRouteKey ] = nil
193
+ }
175
194
}
176
195
}
177
196
@@ -403,6 +422,77 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
403
422
return serverBlocks [1 :], nil
404
423
}
405
424
425
+ // extractNamedRoutes pulls out any named route server blocks
426
+ // so they don't get parsed as sites, and stores them in options
427
+ // for later.
428
+ func (ServerType ) extractNamedRoutes (
429
+ serverBlocks []serverBlock ,
430
+ options map [string ]any ,
431
+ warnings * []caddyconfig.Warning ,
432
+ ) ([]serverBlock , error ) {
433
+ namedRoutes := map [string ]* caddyhttp.Route {}
434
+
435
+ gc := counter {new (int )}
436
+ state := make (map [string ]any )
437
+
438
+ // copy the server blocks so we can
439
+ // splice out the named route ones
440
+ filtered := append ([]serverBlock {}, serverBlocks ... )
441
+ index := - 1
442
+
443
+ for _ , sb := range serverBlocks {
444
+ index ++
445
+ if ! sb .block .IsNamedRoute {
446
+ continue
447
+ }
448
+
449
+ // splice out this block, because we know it's not a real server
450
+ filtered = append (filtered [:index ], filtered [index + 1 :]... )
451
+ index --
452
+
453
+ if len (sb .block .Segments ) == 0 {
454
+ continue
455
+ }
456
+
457
+ // zip up all the segments since ParseSegmentAsSubroute
458
+ // was designed to take a directive+
459
+ wholeSegment := caddyfile.Segment {}
460
+ for _ , segment := range sb .block .Segments {
461
+ wholeSegment = append (wholeSegment , segment ... )
462
+ }
463
+
464
+ h := Helper {
465
+ Dispenser : caddyfile .NewDispenser (wholeSegment ),
466
+ options : options ,
467
+ warnings : warnings ,
468
+ matcherDefs : nil ,
469
+ parentBlock : sb .block ,
470
+ groupCounter : gc ,
471
+ State : state ,
472
+ }
473
+
474
+ handler , err := ParseSegmentAsSubroute (h )
475
+ if err != nil {
476
+ return nil , err
477
+ }
478
+ subroute := handler .(* caddyhttp.Subroute )
479
+ route := caddyhttp.Route {}
480
+
481
+ if len (subroute .Routes ) == 1 && len (subroute .Routes [0 ].MatcherSetsRaw ) == 0 {
482
+ // if there's only one route with no matcher, then we can simplify
483
+ route .HandlersRaw = append (route .HandlersRaw , subroute .Routes [0 ].HandlersRaw [0 ])
484
+ } else {
485
+ // otherwise we need the whole subroute
486
+ route .HandlersRaw = []json.RawMessage {caddyconfig .JSONModuleObject (handler , "handler" , subroute .CaddyModule ().ID .Name (), h .warnings )}
487
+ }
488
+
489
+ namedRoutes [sb .block .Keys [0 ]] = & route
490
+ }
491
+ options ["named_routes" ] = namedRoutes
492
+
493
+ return filtered , nil
494
+ }
495
+
406
496
// serversFromPairings creates the servers for each pairing of addresses
407
497
// to server blocks. Each pairing is essentially a server definition.
408
498
func (st * ServerType ) serversFromPairings (
@@ -542,6 +632,24 @@ func (st *ServerType) serversFromPairings(
542
632
}
543
633
}
544
634
635
+ // add named routes to the server if 'invoke' was used inside of it
636
+ configuredNamedRoutes := options ["named_routes" ].(map [string ]* caddyhttp.Route )
637
+ for _ , sblock := range p .serverBlocks {
638
+ if len (sblock .pile [namedRouteKey ]) == 0 {
639
+ continue
640
+ }
641
+ for _ , value := range sblock .pile [namedRouteKey ] {
642
+ if srv .NamedRoutes == nil {
643
+ srv .NamedRoutes = map [string ]* caddyhttp.Route {}
644
+ }
645
+ name := value .Value .(string )
646
+ if configuredNamedRoutes [name ] == nil {
647
+ return nil , fmt .Errorf ("cannot invoke named route '%s', which was not defined" , name )
648
+ }
649
+ srv .NamedRoutes [name ] = configuredNamedRoutes [name ]
650
+ }
651
+ }
652
+
545
653
// create a subroute for each site in the server block
546
654
for _ , sblock := range p .serverBlocks {
547
655
matcherSetsEnc , err := st .compileEncodedMatcherSets (sblock )
@@ -1469,6 +1577,7 @@ type sbAddrAssociation struct {
1469
1577
}
1470
1578
1471
1579
const matcherPrefix = "@"
1580
+ const namedRouteKey = "named_route"
1472
1581
1473
1582
// Interface guard
1474
1583
var _ caddyfile.ServerType = (* ServerType )(nil )
0 commit comments