From 23cc26d585bfbbe06ae8d2f46fa64956e368baac Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Sun, 16 Feb 2020 22:24:20 -0700 Subject: httpcaddyfile: 'handle_errors' directive Not sure I love the name of the directive; might change it later. --- caddyconfig/httpcaddyfile/builtins.go | 44 ++++++++++------------------- caddyconfig/httpcaddyfile/directives.go | 33 ++++++++++++++++++++++ caddyconfig/httpcaddyfile/httptype.go | 49 +++++++++++++++++++++++++-------- 3 files changed, 85 insertions(+), 41 deletions(-) (limited to 'caddyconfig/httpcaddyfile') diff --git a/caddyconfig/httpcaddyfile/builtins.go b/caddyconfig/httpcaddyfile/builtins.go index b758c39..7ad4a20 100644 --- a/caddyconfig/httpcaddyfile/builtins.go +++ b/caddyconfig/httpcaddyfile/builtins.go @@ -35,7 +35,8 @@ func init() { RegisterHandlerDirective("redir", parseRedir) RegisterHandlerDirective("respond", parseRespond) RegisterHandlerDirective("route", parseRoute) - RegisterHandlerDirective("handle", parseHandle) + RegisterHandlerDirective("handle", parseSegmentAsSubroute) + RegisterDirective("handle_errors", parseHandleErrors) } // parseBind parses the bind directive. Syntax: @@ -235,7 +236,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) { return nil, h.Errf("getting DNS provider module named '%s': %v", provName, err) } mgr.Challenges.DNSRaw = caddyconfig.JSONModuleObject(dnsProvModule.New(), "provider", provName, h.warnings) - + case "ca_root": arg := h.RemainingArgs() if len(arg) != 1 { @@ -387,36 +388,21 @@ func parseRoute(h Helper) (caddyhttp.MiddlewareHandler, error) { return sr, nil } -// parseHandle parses the route directive. func parseHandle(h Helper) (caddyhttp.MiddlewareHandler, error) { - var allResults []ConfigValue - - for h.Next() { - for nesting := h.Nesting(); h.NextBlock(nesting); { - dir := h.Val() - - dirFunc, ok := registeredDirectives[dir] - if !ok { - return nil, h.Errf("unrecognized directive: %s", dir) - } - - subHelper := h - subHelper.Dispenser = h.NewFromNextSegment() - - results, err := dirFunc(subHelper) - if err != nil { - return nil, h.Errf("parsing caddyfile tokens for '%s': %v", dir, err) - } - for _, result := range results { - result.directive = dir - allResults = append(allResults, result) - } - } + return parseSegmentAsSubroute(h) +} - return buildSubroute(allResults, h.groupCounter) +func parseHandleErrors(h Helper) ([]ConfigValue, error) { + subroute, err := parseSegmentAsSubroute(h) + if err != nil { + return nil, err } - - return nil, nil + return []ConfigValue{ + { + Class: "error_route", + Value: subroute, + }, + }, nil } var tagCounter = 0 diff --git a/caddyconfig/httpcaddyfile/directives.go b/caddyconfig/httpcaddyfile/directives.go index 035dcbe..3c03d30 100644 --- a/caddyconfig/httpcaddyfile/directives.go +++ b/caddyconfig/httpcaddyfile/directives.go @@ -37,6 +37,7 @@ var directiveOrder = []string{ "uri_replace", "try_files", + // middleware handlers that typically wrap responses "basicauth", "header", "request_header", @@ -46,6 +47,7 @@ var directiveOrder = []string{ "handle", "route", + // handlers that typically respond to requests "respond", "reverse_proxy", "php_fastcgi", @@ -291,6 +293,37 @@ func sortRoutes(routes []ConfigValue) { }) } +// parseSegmentAsSubroute parses the segment such that its subdirectives +// are themselves treated as directives, from which a subroute is built +// and returned. +func parseSegmentAsSubroute(h Helper) (caddyhttp.MiddlewareHandler, error) { + var allResults []ConfigValue + for h.Next() { + for nesting := h.Nesting(); h.NextBlock(nesting); { + dir := h.Val() + + dirFunc, ok := registeredDirectives[dir] + if !ok { + return nil, h.Errf("unrecognized directive: %s", dir) + } + + subHelper := h + subHelper.Dispenser = h.NewFromNextSegment() + + results, err := dirFunc(subHelper) + if err != nil { + return nil, h.Errf("parsing caddyfile tokens for '%s': %v", dir, err) + } + for _, result := range results { + result.directive = dir + allResults = append(allResults, result) + } + } + return buildSubroute(allResults, h.groupCounter) // TODO: should we move this outside the loop? + } + return nil, nil +} + // serverBlock pairs a Caddyfile server block // with a "pile" of config values, keyed by class // name. diff --git a/caddyconfig/httpcaddyfile/httptype.go b/caddyconfig/httpcaddyfile/httptype.go index f93b8c5..7c13794 100644 --- a/caddyconfig/httpcaddyfile/httptype.go +++ b/caddyconfig/httpcaddyfile/httptype.go @@ -474,18 +474,18 @@ func (st *ServerType) serversFromPairings( return nil, err } - if len(matcherSetsEnc) == 0 && len(p.serverBlocks) == 1 { - // no need to wrap the handlers in a subroute if this is - // the only server block and there is no matcher for it - srv.Routes = append(srv.Routes, siteSubroute.Routes...) - } else { - srv.Routes = append(srv.Routes, caddyhttp.Route{ - MatcherSetsRaw: matcherSetsEnc, - HandlersRaw: []json.RawMessage{ - caddyconfig.JSONModuleObject(siteSubroute, "handler", "subroute", warnings), - }, - Terminal: true, // only first matching site block should be evaluated - }) + // add the site block's route(s) to the server + srv.Routes = appendSubrouteToRouteList(srv.Routes, siteSubroute, matcherSetsEnc, p, warnings) + + // if error routes are defined, add those too + if errorSubrouteVals, ok := sblock.pile["error_route"]; ok { + if srv.Errors == nil { + srv.Errors = new(caddyhttp.HTTPErrorConfig) + } + for _, val := range errorSubrouteVals { + sr := val.Value.(*caddyhttp.Subroute) + srv.Errors.Routes = appendSubrouteToRouteList(srv.Errors.Routes, sr, matcherSetsEnc, p, warnings) + } } } @@ -497,6 +497,31 @@ func (st *ServerType) serversFromPairings( return servers, nil } +// appendSubrouteToRouteList appends the routes in subroute +// to the routeList, optionally qualified by matchers. +func appendSubrouteToRouteList(routeList caddyhttp.RouteList, + subroute *caddyhttp.Subroute, + matcherSetsEnc []caddy.ModuleMap, + p sbAddrAssociation, + warnings *[]caddyconfig.Warning) caddyhttp.RouteList { + if len(matcherSetsEnc) == 0 && len(p.serverBlocks) == 1 { + // no need to wrap the handlers in a subroute if this is + // the only server block and there is no matcher for it + routeList = append(routeList, subroute.Routes...) + } else { + routeList = append(routeList, caddyhttp.Route{ + MatcherSetsRaw: matcherSetsEnc, + HandlersRaw: []json.RawMessage{ + caddyconfig.JSONModuleObject(subroute, "handler", "subroute", warnings), + }, + Terminal: true, // only first matching site block should be evaluated + }) + } + return routeList +} + +// buildSubroute turns the config values, which are expected to be routes +// into a clean and orderly subroute that has all the routes within it. func buildSubroute(routes []ConfigValue, groupCounter counter) (*caddyhttp.Subroute, error) { for _, val := range routes { if !directiveIsOrdered(val.directive) { -- cgit v1.2.3