41
func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
42
options map[string]interface{}) (*caddy.Config, []caddyconfig.Warning, error) {
43
var warnings []caddyconfig.Warning
44
- groupCounter := new(int)
44
+ gc := counter{new(int)}
45
46
var serverBlocks []serverBlock
47
for _, sblock := range originalServerBlocks {
146
warnings: &warnings,
147
matcherDefs: matcherDefs,
148
parentBlock: sb.block,
149
- groupCounter: groupCounter,
149
+ groupCounter: gc,
150
})
151
if err != nil {
152
return nil, warnings, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
169
170
// each pairing of listener addresses to list of server
171
// blocks is basically a server definition
172
- servers, err := st.serversFromPairings(pairings, options, &warnings)
172
+ servers, err := st.serversFromPairings(pairings, options, &warnings, gc)
173
if err != nil {
174
return nil, warnings, err
175
}
306
pairings []sbAddrAssociation,
307
options map[string]interface{},
308
warnings *[]caddyconfig.Warning,
309
+ groupCounter counter,
310
) (map[string]*caddyhttp.Server, error) {
311
servers := make(map[string]*caddyhttp.Server)
312
321
// case their matchers overlap; we do this somewhat naively by
322
// descending sort by length of host then path
323
sort.SliceStable(p.serverBlocks, func(i, j int) bool {
323
- // TODO: we could pre-process the lengths for efficiency,
324
+ // TODO: we could pre-process the specificities for efficiency,
325
// but I don't expect many blocks will have SO many keys...
326
var iLongestPath, jLongestPath string
327
var iLongestHost, jLongestHost string
328
for _, key := range p.serverBlocks[i].block.Keys {
329
addr, _ := ParseAddress(key)
329
- if length(addr.Host) > length(iLongestHost) {
330
+ if specificity(addr.Host) > specificity(iLongestHost) {
331
iLongestHost = addr.Host
332
}
332
- if len(addr.Path) > len(iLongestPath) {
333
+ if specificity(addr.Path) > specificity(iLongestPath) {
334
iLongestPath = addr.Path
335
}
336
}
337
for _, key := range p.serverBlocks[j].block.Keys {
338
addr, _ := ParseAddress(key)
338
- if length(addr.Host) > length(jLongestHost) {
339
+ if specificity(addr.Host) > specificity(jLongestHost) {
340
jLongestHost = addr.Host
341
}
341
- if len(addr.Path) > len(jLongestPath) {
342
+ if specificity(addr.Path) > specificity(jLongestPath) {
343
jLongestPath = addr.Path
344
}
345
}
345
- if length(iLongestHost) == length(jLongestHost) {
346
+ if specificity(iLongestHost) == specificity(jLongestHost) {
347
return len(iLongestPath) > len(jLongestPath)
348
}
348
- return length(iLongestHost) > length(jLongestHost)
349
+ return specificity(iLongestHost) > specificity(jLongestHost)
350
})
351
352
// create a subroute for each site in the server block
356
return nil, fmt.Errorf("server block %v: compiling matcher sets: %v", sblock.block.Keys, err)
357
}
358
358
- siteSubroute := new(caddyhttp.Subroute)
359
-
359
// tls: connection policies and toggle auto HTTPS
361
-
360
autoHTTPSQualifiedHosts, err := st.autoHTTPSHosts(sblock)
361
if err != nil {
362
return nil, err
389
// TODO: consolidate equal conn policies
390
}
391
394
- // vars: make sure these are linked in first so future
395
- // routes can use the variables they define
396
- for _, cfgVal := range sblock.pile["var"] {
397
- siteSubroute.Routes = append(siteSubroute.Routes, cfgVal.Value.(caddyhttp.Route))
398
- }
399
-
392
// set up each handler directive, making sure to honor directive order
393
dirRoutes := sblock.pile["route"]
402
- sortRoutes(dirRoutes)
403
-
404
- // add all the routes piled in from directives
405
- for _, r := range dirRoutes {
406
- // as a special case, group rewrite directives so that they are mutually exclusive;
407
- // this means that only the first matching rewrite will be evaluated, and that's
408
- // probably a good thing, since there should never be a need to do more than one
409
- // rewrite (I think?), and cascading rewrites smell bad... imagine these rewrites:
410
- // rewrite /docs/json/* /docs/json/index.html
411
- // rewrite /docs/* /docs/index.html
412
- // (We use this on the Caddy website, or at least we did once.) The first rewrite's
413
- // result is also matched by the second rewrite, making the first rewrite pointless.
414
- // See issue #2959.
415
- if r.directive == "rewrite" {
416
- route := r.Value.(caddyhttp.Route)
417
- route.Group = "rewriting"
418
- r.Value = route
419
- }
420
-
421
- siteSubroute.Routes = append(siteSubroute.Routes, r.Value.(caddyhttp.Route))
394
+ siteSubroute, err := buildSubroute(dirRoutes, groupCounter)
395
+ if err != nil {
396
+ return nil, err
397
}
398
424
- siteSubroute.Routes = consolidateRoutes(siteSubroute.Routes)
425
-
399
if len(matcherSetsEnc) == 0 && len(p.serverBlocks) == 1 {
400
// no need to wrap the handlers in a subroute if this is
401
// the only server block and there is no matcher for it
419
return servers, nil
420
}
421
422
+func buildSubroute(routes []ConfigValue, groupCounter counter) (*caddyhttp.Subroute, error) {
423
+ for _, val := range routes {
424
+ if !directiveIsOrdered(val.directive) {
425
+ return nil, fmt.Errorf("directive '%s' is not ordered, so it cannot be used here", val.directive)
426
+ }
427
+ }
428
+
429
+ sortRoutes(routes)
430
+
431
+ subroute := new(caddyhttp.Subroute)
432
+
433
+ // get a group name for rewrite directives, if needed
434
+ var rewriteGroupName string
435
+ var rewriteCount int
436
+ for _, r := range routes {
437
+ if r.directive == "rewrite" {
438
+ rewriteCount++
439
+ if rewriteCount > 1 {
440
+ break
441
+ }
442
+ }
443
+ }
444
+ if rewriteCount > 1 {
445
+ rewriteGroupName = groupCounter.nextGroup()
446
+ }
447
+
448
+ // get a group name for handle blocks, if needed
449
+ var handleGroupName string
450
+ var handleCount int
451
+ for _, r := range routes {
452
+ if r.directive == "handle" {
453
+ handleCount++
454
+ if handleCount > 1 {
455
+ break
456
+ }
457
+ }
458
+ }
459
+ if handleCount > 1 {
460
+ handleGroupName = groupCounter.nextGroup()
461
+ }
462
+
463
+ // add all the routes piled in from directives
464
+ for _, r := range routes {
465
+ // as a special case, group rewrite directives so that they are mutually exclusive;
466
+ // this means that only the first matching rewrite will be evaluated, and that's
467
+ // probably a good thing, since there should never be a need to do more than one
468
+ // rewrite (I think?), and cascading rewrites smell bad... imagine these rewrites:
469
+ // rewrite /docs/json/* /docs/json/index.html
470
+ // rewrite /docs/* /docs/index.html
471
+ // (We use this on the Caddy website, or at least we did once.) The first rewrite's
472
+ // result is also matched by the second rewrite, making the first rewrite pointless.
473
+ // See issue #2959.
474
+ if r.directive == "rewrite" {
475
+ route := r.Value.(caddyhttp.Route)
476
+ route.Group = rewriteGroupName
477
+ r.Value = route
478
+ }
479
+
480
+ // handle blocks are also mutually exclusive by definition
481
+ if r.directive == "handle" {
482
+ route := r.Value.(caddyhttp.Route)
483
+ route.Group = handleGroupName
484
+ r.Value = route
485
+ }
486
+
487
+ switch route := r.Value.(type) {
488
+ case caddyhttp.Subroute:
489
+ // if a route-class config value is actually a Subroute handler
490
+ // with nothing but a list of routes, then it is the intention
491
+ // of the directive to keep these handlers together and in this
492
+ // same order, but not necessarily in a subroute (if it wanted
493
+ // to keep them in a subroute, the directive would have returned
494
+ // a route with a Subroute as its handler); this is useful to
495
+ // keep multiple handlers/routes together and in the same order
496
+ // so that the sorting procedure we did above doesn't reorder them
497
+ if route.Errors != nil {
498
+ // if error handlers are also set, this is confusing; it's
499
+ // probably supposed to be wrapped in a Route and encoded
500
+ // as a regular handler route... programmer error.
501
+ panic("found subroute with more than just routes; perhaps it should have been wrapped in a route?")
502
+ }
503
+ subroute.Routes = append(subroute.Routes, route.Routes...)
504
+ case caddyhttp.Route:
505
+ subroute.Routes = append(subroute.Routes, route)
506
+ }
507
+ }
508
+
509
+ subroute.Routes = consolidateRoutes(subroute.Routes)
510
+
511
+ return subroute, nil
512
+}
513
+
514
func (st ServerType) autoHTTPSHosts(sb serverBlock) ([]string, error) {
515
// get the hosts for this server block...
516
hosts, err := st.hostsFromServerBlockKeys(sb.block)
586
}
587
return m, true, nil
588
}
524
-
589
return nil, false, nil
590
}
591
723
return intVal
724
}
725
662
-// length returns len(s) minus any wildcards (*). Basically,
663
-// it's a length count that penalizes the use of wildcards.
664
-// This is useful for comparing hostnames, but probably not
665
-// paths so much (for example, '*.example.com' is clearly
666
-// less specific than 'a.example.com', but is '/a' more or
667
-// less specific than '/a*'?).
668
-func length(s string) int {
669
- return len(s) - strings.Count(s, "*")
726
+// specifity returns len(s) minus any wildcards (*) and
727
+// placeholders ({...}). Basically, it's a length count
728
+// that penalizes the use of wildcards and placeholders.
729
+// This is useful for comparing hostnames and paths.
730
+// However, wildcards in paths are not a sure answer to
731
+// the question of specificity. For exmaple,
732
+// '*.example.com' is clearly less specific than
733
+// 'a.example.com', but is '/a' more or less specific
734
+// than '/a*'?
735
+func specificity(s string) int {
736
+ l := len(s) - strings.Count(s, "*")
737
+ for len(s) > 0 {
738
+ start := strings.Index(s, "{")
739
+ if start < 0 {
740
+ return l
741
+ }
742
+ end := strings.Index(s[start:], "}") + start + 1
743
+ if end <= start {
744
+ return l
745
+ }
746
+ l -= end - start
747
+ s = s[end:]
748
+ }
749
+ return l
750
+}
751
+
752
+type counter struct {
753
+ n *int
754
+}
755
+
756
+func (c counter) nextGroup() string {
757
+ name := fmt.Sprintf("group%d", *c.n)
758
+ *c.n++
759
+ return name
760
}
761
762
type matcherSetAndTokens struct {