114
}
115
116
// extract matcher definitions
117
- d := sb.block.DispenseDirective("matcher")
118
- matcherDefs, err := parseMatcherDefinitions(d)
119
- if err != nil {
120
- return nil, warnings, err
117
+ matcherDefs := make(map[string]caddy.ModuleMap)
118
+ for _, segment := range sb.block.Segments {
119
+ if dir := segment.Directive(); strings.HasPrefix(dir, matcherPrefix) {
120
+ d := sb.block.DispenseDirective(dir)
121
+ err := parseMatcherDefinitions(d, matcherDefs)
122
+ if err != nil {
123
+ return nil, warnings, err
124
+ }
125
+ }
126
}
127
128
for _, segment := range sb.block.Segments {
129
dir := segment.Directive()
125
- if dir == "matcher" {
126
- // TODO: This is a special case because we pre-processed it; handle this better
130
+
131
+ if strings.HasPrefix(dir, matcherPrefix) {
132
+ // matcher definitions were pre-processed
133
continue
134
}
129
- if dirFunc, ok := registeredDirectives[dir]; ok {
130
- results, err := dirFunc(Helper{
131
- Dispenser: caddyfile.NewDispenser(segment),
132
- options: options,
133
- warnings: &warnings,
134
- matcherDefs: matcherDefs,
135
- parentBlock: sb.block,
136
- })
137
- if err != nil {
138
- return nil, warnings, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
139
- }
140
- for _, result := range results {
141
- result.directive = dir
142
- sb.pile[result.Class] = append(sb.pile[result.Class], result)
143
- }
144
- } else {
135
+
136
+ dirFunc, ok := registeredDirectives[dir]
137
+ if !ok {
138
tkn := segment[0]
139
return nil, warnings, fmt.Errorf("%s:%d: unrecognized directive: %s", tkn.File, tkn.Line, dir)
140
}
141
+
142
+ results, err := dirFunc(Helper{
143
+ Dispenser: caddyfile.NewDispenser(segment),
144
+ options: options,
145
+ warnings: &warnings,
146
+ matcherDefs: matcherDefs,
147
+ parentBlock: sb.block,
148
+ })
149
+ if err != nil {
150
+ return nil, warnings, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
151
+ }
152
+ for _, result := range results {
153
+ result.directive = dir
154
+ sb.pile[result.Class] = append(sb.pile[result.Class], result)
155
+ }
156
}
157
}
158
380
}
381
sort.SliceStable(dirRoutes, func(i, j int) bool {
382
iDir, jDir := dirRoutes[i].directive, dirRoutes[j].directive
383
+ if iDir == jDir {
384
+ // TODO: we really need to refactor this into a separate function or method...
385
+ // sub-sort by path matcher length, if there's only one
386
+ iRoute := dirRoutes[i].Value.(caddyhttp.Route)
387
+ jRoute := dirRoutes[j].Value.(caddyhttp.Route)
388
+ if len(iRoute.MatcherSetsRaw) == 1 && len(jRoute.MatcherSetsRaw) == 1 {
389
+ // for slightly better efficiency, only decode the path matchers once,
390
+ // then just store them arbitrarily in the decoded MatcherSets field,
391
+ // ours should be the only thing in there
392
+ var iPM, jPM caddyhttp.MatchPath
393
+ if len(iRoute.MatcherSets) == 1 {
394
+ iPM = iRoute.MatcherSets[0][0].(caddyhttp.MatchPath)
395
+ }
396
+ if len(jRoute.MatcherSets) == 1 {
397
+ jPM = jRoute.MatcherSets[0][0].(caddyhttp.MatchPath)
398
+ }
399
+ // if it's our first time seeing this route's path matcher, decode it
400
+ if iPM == nil {
401
+ var pathMatcher caddyhttp.MatchPath
402
+ _ = json.Unmarshal(iRoute.MatcherSetsRaw[0]["path"], &pathMatcher)
403
+ iRoute.MatcherSets = caddyhttp.MatcherSets{{pathMatcher}}
404
+ iPM = pathMatcher
405
+ }
406
+ if jPM == nil {
407
+ var pathMatcher caddyhttp.MatchPath
408
+ _ = json.Unmarshal(jRoute.MatcherSetsRaw[0]["path"], &pathMatcher)
409
+ jRoute.MatcherSets = caddyhttp.MatcherSets{{pathMatcher}}
410
+ jPM = pathMatcher
411
+ }
412
+ // finally, if there is only one path in the
413
+ // matcher, sort by longer path first
414
+ if len(iPM) == 1 && len(jPM) == 1 {
415
+ return len(iPM[0]) > len(jPM[0])
416
+ }
417
+ }
418
+ }
419
return dirPositions[iDir] < dirPositions[jDir]
420
})
421
}
422
+
423
+ // add all the routes piled in from directives
424
for _, r := range dirRoutes {
425
+ // as a special case, group rewrite directives so that they are mutually exclusive;
426
+ // this means that only the first matching rewrite will be evaluated, and that's
427
+ // probably a good thing, since there should never be a need to do more than one
428
+ // rewrite (I think?), and cascading rewrites smell bad... imagine these rewrites:
429
+ // rewrite /docs/json/* /docs/json/index.html
430
+ // rewrite /docs/* /docs/index.html
431
+ // (We use this on the Caddy website, or at least we did once.) The first rewrite's
432
+ // result is also matched by the second rewrite, making the first rewrite pointless.
433
+ // See issue #2959.
434
+ if r.directive == "rewrite" {
435
+ route := r.Value.(caddyhttp.Route)
436
+ route.Group = "rewriting"
437
+ r.Value = route
438
+ }
439
+
440
handlerSubroute.Routes = append(handlerSubroute.Routes, r.Value.(caddyhttp.Route))
441
}
442
541
if tkn.Text == "*" {
542
// match all requests == no matchers, so nothing to do
543
return nil, true, nil
483
- } else if strings.HasPrefix(tkn.Text, "/") || strings.HasPrefix(tkn.Text, "=/") {
544
+ } else if strings.HasPrefix(tkn.Text, "/") {
545
// convenient way to specify a single path match
546
return caddy.ModuleMap{
547
"path": caddyconfig.JSON(caddyhttp.MatchPath{tkn.Text}, warnings),
548
}, true, nil
488
- } else if strings.HasPrefix(tkn.Text, "match:") {
549
+ } else if strings.HasPrefix(tkn.Text, matcherPrefix) {
550
// pre-defined matcher
490
- matcherName := strings.TrimPrefix(tkn.Text, "match:")
491
- m, ok := matcherDefs[matcherName]
551
+ m, ok := matcherDefs[tkn.Text]
552
if !ok {
493
- return nil, false, fmt.Errorf("unrecognized matcher name: %+v", matcherName)
553
+ return nil, false, fmt.Errorf("unrecognized matcher name: %+v", tkn.Text)
554
}
555
return m, true, nil
556
}
637
return matcherSetsEnc, nil
638
}
639
580
-func parseMatcherDefinitions(d *caddyfile.Dispenser) (map[string]caddy.ModuleMap, error) {
581
- matchers := make(map[string]caddy.ModuleMap)
640
+func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.ModuleMap) error {
641
for d.Next() {
642
definitionName := d.Val()
643
+
644
+ if _, ok := matchers[definitionName]; ok {
645
+ return fmt.Errorf("matcher is defined more than once: %s", definitionName)
646
+ }
647
+ matchers[definitionName] = make(caddy.ModuleMap)
648
+
649
for nesting := d.Nesting(); d.NextBlock(nesting); {
650
matcherName := d.Val()
651
mod, err := caddy.GetModule("http.matchers." + matcherName)
652
if err != nil {
588
- return nil, fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
653
+ return fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
654
}
655
unm, ok := mod.New().(caddyfile.Unmarshaler)
656
if !ok {
592
- return nil, fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName)
657
+ return fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName)
658
}
659
err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())
660
if err != nil {
596
- return nil, err
661
+ return err
662
}
663
rm, ok := unm.(caddyhttp.RequestMatcher)
664
if !ok {
600
- return nil, fmt.Errorf("matcher module '%s' is not a request matcher", matcherName)
601
- }
602
- if _, ok := matchers[definitionName]; !ok {
603
- matchers[definitionName] = make(caddy.ModuleMap)
665
+ return fmt.Errorf("matcher module '%s' is not a request matcher", matcherName)
666
}
667
matchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)
668
}
669
}
608
- return matchers, nil
670
+ return nil
671
}
672
673
func encodeMatcherSet(matchers map[string]caddyhttp.RequestMatcher) (caddy.ModuleMap, error) {
705
serverBlocks []serverBlock
706
}
707
708
+const matcherPrefix = "@"
709
+
710
// Interface guard
711
var _ caddyfile.ServerType = (*ServerType)(nil)