From 25d2b4bf2927bf69ddce582d33339ef7d13cb6bf Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 2 Oct 2020 14:23:40 -0600 Subject: map: Reimplement; multiple outputs; optimize --- modules/caddyhttp/map/caddyfile.go | 86 +++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 29 deletions(-) (limited to 'modules/caddyhttp/map/caddyfile.go') diff --git a/modules/caddyhttp/map/caddyfile.go b/modules/caddyhttp/map/caddyfile.go index 5737971..67c148b 100644 --- a/modules/caddyhttp/map/caddyfile.go +++ b/modules/caddyhttp/map/caddyfile.go @@ -15,6 +15,8 @@ package maphandler import ( + "strings" + "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp" ) @@ -23,49 +25,75 @@ func init() { httpcaddyfile.RegisterHandlerDirective("map", parseCaddyfile) } -// parseCaddyfile sets up the handler for a map from Caddyfile tokens. Syntax: +// parseCaddyfile sets up the map handler from Caddyfile tokens. Syntax: // -// map { -// [default ] - used if not match is found -// [ ] - regular expression to match against the source find and the matching replacement value -// ... +// map [] { +// [~] +// default // } // -// The map takes a source variable and maps it into the dest variable. The mapping process -// will check the source variable for the first successful match against a list of regular expressions. -// If a successful match is found the dest variable will contain the replacement value. -// If no successful match is found and the default is specified then the dest will contain the default value. +// If the input value is prefixed with a tilde (~), then the input will be parsed as a +// regular expression. // +// The number of outputs for each mapping must not be more than the number of destinations. +// However, for convenience, there may be fewer outputs than destinations and any missing +// outputs will be filled in implicitly. func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { - m := new(Handler) + var handler Handler for h.Next() { - // first see if source and dest are configured - if h.NextArg() { - m.Source = h.Val() - if h.NextArg() { - m.Destination = h.Val() - } + // source + if !h.NextArg() { + return nil, h.ArgErr() + } + handler.Source = h.Val() + + // destinations + handler.Destinations = h.RemainingArgs() + if len(handler.Destinations) == 0 { + return nil, h.Err("missing destination argument(s)") } - // load the rules + // mappings for h.NextBlock(0) { - expression := h.Val() - if expression == "default" { - args := h.RemainingArgs() - if len(args) != 1 { - return m, h.ArgErr() + // defaults are a special case + if h.Val() == "default" { + if len(handler.Defaults) > 0 { + return nil, h.Err("defaults already defined") } - m.Default = args[0] - } else { - args := h.RemainingArgs() - if len(args) != 1 { - return m, h.ArgErr() + handler.Defaults = h.RemainingArgs() + for len(handler.Defaults) < len(handler.Destinations) { + handler.Defaults = append(handler.Defaults, "") } - m.Items = append(m.Items, Item{Expression: expression, Value: args[0]}) + continue + } + + // every other line maps one input to one or more outputs + in := h.Val() + outs := h.RemainingArgs() + + // cannot have more outputs than destinations + if len(outs) > len(handler.Destinations) { + return nil, h.Err("too many outputs") + } + + // for convenience, can have fewer outputs than destinations, but the + // underlying handler won't accept that, so we fill in empty values + for len(outs) < len(handler.Destinations) { + outs = append(outs, "") } + + // create the mapping + mapping := Mapping{Outputs: outs} + if strings.HasPrefix(in, "~") { + mapping.InputRegexp = in[1:] + } else { + mapping.Input = in + } + + handler.Mappings = append(handler.Mappings, mapping) } } - return m, nil + return handler, nil } -- cgit v1.2.3