From 8c5d00b2bc815c182e1a510be6dddc128949bf23 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Tue, 26 May 2020 17:27:51 -0400 Subject: httpcaddyfile: New `handle_path` directive (#3281) * caddyconfig: WIP implementation of handle_path * caddyconfig: Complete the implementation - h.NewRoute was key * caddyconfig: Add handle_path integration test * caddyhttp: Use the path matcher as-is, strip the trailing *, update test --- modules/caddyhttp/rewrite/caddyfile.go | 74 ++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'modules/caddyhttp') diff --git a/modules/caddyhttp/rewrite/caddyfile.go b/modules/caddyhttp/rewrite/caddyfile.go index ee7dd23..950119d 100644 --- a/modules/caddyhttp/rewrite/caddyfile.go +++ b/modules/caddyhttp/rewrite/caddyfile.go @@ -15,9 +15,12 @@ package rewrite import ( + "encoding/json" "strconv" "strings" + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp" ) @@ -25,6 +28,7 @@ import ( func init() { httpcaddyfile.RegisterHandlerDirective("rewrite", parseCaddyfileRewrite) httpcaddyfile.RegisterHandlerDirective("uri", parseCaddyfileURI) + httpcaddyfile.RegisterDirective("handle_path", parseCaddyfileHandlePath) } // parseCaddyfileRewrite sets up a basic rewrite handler from Caddyfile tokens. Syntax: @@ -110,3 +114,73 @@ func parseCaddyfileURI(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, err } return rewr, nil } + +// parseCaddyfileHandlePath parses the handle_path directive. Syntax: +// +// handle_path [] { +// +// } +// +// Only path matchers (with a `/` prefix) are supported as this is a shortcut +// for the handle directive with a strip_prefix rewrite. +func parseCaddyfileHandlePath(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) { + if !h.Next() { + return nil, h.ArgErr() + } + if !h.NextArg() { + return nil, h.ArgErr() + } + + // read the prefix to strip + path := h.Val() + if !strings.HasPrefix(path, "/") { + return nil, h.Errf("path matcher must begin with '/', got %s", path) + } + + // we only want to strip what comes before the '/' if + // the user specified it (e.g. /api/* should only strip /api) + var stripPath string + if strings.HasSuffix(path, "/*") { + stripPath = path[:len(path)-2] + } else if strings.HasSuffix(path, "*") { + stripPath = path[:len(path)-1] + } else { + stripPath = path + } + + // the ParseSegmentAsSubroute function expects the cursor + // to be at the token just before the block opening, + // so we need to rewind because we already read past it + h.Reset() + h.Next() + + // parse the block contents as a subroute handler + handler, err := httpcaddyfile.ParseSegmentAsSubroute(h) + if err != nil { + return nil, err + } + subroute, ok := handler.(*caddyhttp.Subroute) + if !ok { + return nil, h.Errf("segment was not parsed as a subroute") + } + + // make a matcher on the path and everything below it + pathMatcher := caddy.ModuleMap{ + "path": h.JSON(caddyhttp.MatchPath{path}), + } + + // build a route with a rewrite handler to strip the path prefix + route := caddyhttp.Route{ + HandlersRaw: []json.RawMessage{ + caddyconfig.JSONModuleObject(Rewrite{ + StripPathPrefix: stripPath, + }, "handler", "rewrite", nil), + }, + } + + // prepend the route to the subroute + subroute.Routes = append([]caddyhttp.Route{route}, subroute.Routes...) + + // build and return a route from the subroute + return h.NewRoute(pathMatcher, subroute), nil +} -- cgit v1.2.3