httpcaddyfile: 'handle_errors' directive

Not sure I love the name of the directive; might change it later.
This commit is contained in:
Matthew Holt 2020-02-16 22:24:20 -07:00
parent bc2e406572
commit 23cc26d585
No known key found for this signature in database
GPG key ID: 2A349DD577D586A5
3 changed files with 85 additions and 41 deletions

View file

@ -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:
@ -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)
return parseSegmentAsSubroute(h)
}
subHelper := h
subHelper.Dispenser = h.NewFromNextSegment()
results, err := dirFunc(subHelper)
func parseHandleErrors(h Helper) ([]ConfigValue, error) {
subroute, err := parseSegmentAsSubroute(h)
if err != nil {
return nil, h.Errf("parsing caddyfile tokens for '%s': %v", dir, err)
return nil, err
}
for _, result := range results {
result.directive = dir
allResults = append(allResults, result)
}
}
return buildSubroute(allResults, h.groupCounter)
}
return nil, nil
return []ConfigValue{
{
Class: "error_route",
Value: subroute,
},
}, nil
}
var tagCounter = 0

View file

@ -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.

View file

@ -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) {