Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
httpcaddyfile: 'handle_errors' directive
Not sure I love the name of the directive; might change it later.
Matthew Holt committed
Feb 16, 2020 at 22:24 UTC
23cc26d585bfbbe06ae8d2f46fa64956e368baac
3 files changed
+85
-41
caddyconfig/httpcaddyfile/builtins.go
+15
-29
@@ -35,7 +35,8 @@ func init() {
35
RegisterHandlerDirective("redir", parseRedir)
36
RegisterHandlerDirective("respond", parseRespond)
37
RegisterHandlerDirective("route", parseRoute)
38
- RegisterHandlerDirective("handle", parseHandle)
38
+ RegisterHandlerDirective("handle", parseSegmentAsSubroute)
39
+ RegisterDirective("handle_errors", parseHandleErrors)
40
}
41
42
// parseBind parses the bind directive. Syntax:
@@ -235,7 +236,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
236
return nil, h.Errf("getting DNS provider module named '%s': %v", provName, err)
237
}
238
mgr.Challenges.DNSRaw = caddyconfig.JSONModuleObject(dnsProvModule.New(), "provider", provName, h.warnings)
238
-
239
+
240
case "ca_root":
241
arg := h.RemainingArgs()
242
if len(arg) != 1 {
@@ -387,36 +388,21 @@ func parseRoute(h Helper) (caddyhttp.MiddlewareHandler, error) {
388
return sr, nil
389
}
390
390
-// parseHandle parses the route directive.
391
func parseHandle(h Helper) (caddyhttp.MiddlewareHandler, error) {
392
- var allResults []ConfigValue
393
-
394
- for h.Next() {
395
- for nesting := h.Nesting(); h.NextBlock(nesting); {
396
- dir := h.Val()
397
-
398
- dirFunc, ok := registeredDirectives[dir]
399
- if !ok {
400
- return nil, h.Errf("unrecognized directive: %s", dir)
401
- }
402
-
403
- subHelper := h
404
- subHelper.Dispenser = h.NewFromNextSegment()
405
-
406
- results, err := dirFunc(subHelper)
407
- if err != nil {
408
- return nil, h.Errf("parsing caddyfile tokens for '%s': %v", dir, err)
409
- }
410
- for _, result := range results {
411
- result.directive = dir
412
- allResults = append(allResults, result)
413
- }
414
- }
392
+ return parseSegmentAsSubroute(h)
393
+}
394
416
- return buildSubroute(allResults, h.groupCounter)
395
+func parseHandleErrors(h Helper) ([]ConfigValue, error) {
396
+ subroute, err := parseSegmentAsSubroute(h)
397
+ if err != nil {
398
+ return nil, err
399
}
418
-
419
- return nil, nil
400
+ return []ConfigValue{
401
+ {
402
+ Class: "error_route",
403
+ Value: subroute,
404
+ },
405
+ }, nil
406
}
407
408
var tagCounter = 0
caddyconfig/httpcaddyfile/directives.go
+33
@@ -37,6 +37,7 @@ var directiveOrder = []string{
37
"uri_replace",
38
"try_files",
39
40
+ // middleware handlers that typically wrap responses
41
"basicauth",
42
"header",
43
"request_header",
@@ -46,6 +47,7 @@ var directiveOrder = []string{
47
"handle",
48
"route",
49
50
+ // handlers that typically respond to requests
51
"respond",
52
"reverse_proxy",
53
"php_fastcgi",
@@ -291,6 +293,37 @@ func sortRoutes(routes []ConfigValue) {
293
})
294
}
295
296
+// parseSegmentAsSubroute parses the segment such that its subdirectives
297
+// are themselves treated as directives, from which a subroute is built
298
+// and returned.
299
+func parseSegmentAsSubroute(h Helper) (caddyhttp.MiddlewareHandler, error) {
300
+ var allResults []ConfigValue
301
+ for h.Next() {
302
+ for nesting := h.Nesting(); h.NextBlock(nesting); {
303
+ dir := h.Val()
304
+
305
+ dirFunc, ok := registeredDirectives[dir]
306
+ if !ok {
307
+ return nil, h.Errf("unrecognized directive: %s", dir)
308
+ }
309
+
310
+ subHelper := h
311
+ subHelper.Dispenser = h.NewFromNextSegment()
312
+
313
+ results, err := dirFunc(subHelper)
314
+ if err != nil {
315
+ return nil, h.Errf("parsing caddyfile tokens for '%s': %v", dir, err)
316
+ }
317
+ for _, result := range results {
318
+ result.directive = dir
319
+ allResults = append(allResults, result)
320
+ }
321
+ }
322
+ return buildSubroute(allResults, h.groupCounter) // TODO: should we move this outside the loop?
323
+ }
324
+ return nil, nil
325
+}
326
+
327
// serverBlock pairs a Caddyfile server block
328
// with a "pile" of config values, keyed by class
329
// name.
caddyconfig/httpcaddyfile/httptype.go
+37
-12
@@ -474,18 +474,18 @@ func (st *ServerType) serversFromPairings(
474
return nil, err
475
}
476
477
- if len(matcherSetsEnc) == 0 && len(p.serverBlocks) == 1 {
478
- // no need to wrap the handlers in a subroute if this is
479
- // the only server block and there is no matcher for it
480
- srv.Routes = append(srv.Routes, siteSubroute.Routes...)
481
- } else {
482
- srv.Routes = append(srv.Routes, caddyhttp.Route{
483
- MatcherSetsRaw: matcherSetsEnc,
484
- HandlersRaw: []json.RawMessage{
485
- caddyconfig.JSONModuleObject(siteSubroute, "handler", "subroute", warnings),
486
- },
487
- Terminal: true, // only first matching site block should be evaluated
488
- })
477
+ // add the site block's route(s) to the server
478
+ srv.Routes = appendSubrouteToRouteList(srv.Routes, siteSubroute, matcherSetsEnc, p, warnings)
479
+
480
+ // if error routes are defined, add those too
481
+ if errorSubrouteVals, ok := sblock.pile["error_route"]; ok {
482
+ if srv.Errors == nil {
483
+ srv.Errors = new(caddyhttp.HTTPErrorConfig)
484
+ }
485
+ for _, val := range errorSubrouteVals {
486
+ sr := val.Value.(*caddyhttp.Subroute)
487
+ srv.Errors.Routes = appendSubrouteToRouteList(srv.Errors.Routes, sr, matcherSetsEnc, p, warnings)
488
+ }
489
}
490
}
491
@@ -497,6 +497,31 @@ func (st *ServerType) serversFromPairings(
497
return servers, nil
498
}
499
500
+// appendSubrouteToRouteList appends the routes in subroute
501
+// to the routeList, optionally qualified by matchers.
502
+func appendSubrouteToRouteList(routeList caddyhttp.RouteList,
503
+ subroute *caddyhttp.Subroute,
504
+ matcherSetsEnc []caddy.ModuleMap,
505
+ p sbAddrAssociation,
506
+ warnings *[]caddyconfig.Warning) caddyhttp.RouteList {
507
+ if len(matcherSetsEnc) == 0 && len(p.serverBlocks) == 1 {
508
+ // no need to wrap the handlers in a subroute if this is
509
+ // the only server block and there is no matcher for it
510
+ routeList = append(routeList, subroute.Routes...)
511
+ } else {
512
+ routeList = append(routeList, caddyhttp.Route{
513
+ MatcherSetsRaw: matcherSetsEnc,
514
+ HandlersRaw: []json.RawMessage{
515
+ caddyconfig.JSONModuleObject(subroute, "handler", "subroute", warnings),
516
+ },
517
+ Terminal: true, // only first matching site block should be evaluated
518
+ })
519
+ }
520
+ return routeList
521
+}
522
+
523
+// buildSubroute turns the config values, which are expected to be routes
524
+// into a clean and orderly subroute that has all the routes within it.
525
func buildSubroute(routes []ConfigValue, groupCounter counter) (*caddyhttp.Subroute, error) {
526
for _, val := range routes {
527
if !directiveIsOrdered(val.directive) {