From 5e2f1b5ced5e7153f9748477612cf46188470ca7 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Mon, 15 Jan 2024 11:57:08 -0500 Subject: [PATCH] httpcaddyfile: Rewrite `root` and `rewrite` parsing to allow omitting matcher (#5844) --- caddyconfig/httpcaddyfile/builtins.go | 45 +++++-- .../rewrite_directive_permutations.txt | 112 ++++++++++++++++++ .../root_directive_permutations.txt | 108 +++++++++++++++++ modules/caddyhttp/rewrite/caddyfile.go | 43 ++++++- 4 files changed, 296 insertions(+), 12 deletions(-) create mode 100644 caddytest/integration/caddyfile_adapt/rewrite_directive_permutations.txt create mode 100644 caddytest/integration/caddyfile_adapt/root_directive_permutations.txt diff --git a/caddyconfig/httpcaddyfile/builtins.go b/caddyconfig/httpcaddyfile/builtins.go index 5bfe434c..3b56e073 100644 --- a/caddyconfig/httpcaddyfile/builtins.go +++ b/caddyconfig/httpcaddyfile/builtins.go @@ -41,7 +41,7 @@ func init() { RegisterDirective("bind", parseBind) RegisterDirective("tls", parseTLS) RegisterHandlerDirective("fs", parseFilesystem) - RegisterHandlerDirective("root", parseRoot) + RegisterDirective("root", parseRoot) RegisterHandlerDirective("vars", parseVars) RegisterHandlerDirective("redir", parseRedir) RegisterHandlerDirective("respond", parseRespond) @@ -645,18 +645,45 @@ func parseTLS(h Helper) ([]ConfigValue, error) { // parseRoot parses the root directive. Syntax: // // root [] -func parseRoot(h Helper) (caddyhttp.MiddlewareHandler, error) { - var root string - for h.Next() { +func parseRoot(h Helper) ([]ConfigValue, error) { + // consume directive name + if !h.NextArg() { + return nil, h.ArgErr() + } + + // count the tokens to determine what to do + argsCount := h.CountRemainingArgs() + if argsCount == 0 { + return nil, h.Errf("too few arguments; must have at least a root path") + } + if argsCount > 2 { + return nil, h.Errf("too many arguments; should only be a matcher and a path") + } + + // with only one arg, assume it's a root path with no matcher token + if argsCount == 1 { if !h.NextArg() { return nil, h.ArgErr() } - root = h.Val() - if h.NextArg() { - return nil, h.ArgErr() - } + return h.NewRoute(nil, caddyhttp.VarsMiddleware{"root": h.Val()}), nil } - return caddyhttp.VarsMiddleware{"root": root}, nil + + // parse the matcher token into a matcher set + userMatcherSet, err := h.ExtractMatcherSet() + if err != nil { + return nil, err + } + + // consume directive name, again, because extracting matcher does a reset + if !h.NextArg() { + return nil, h.ArgErr() + } + // advance to the root path + if !h.NextArg() { + return nil, h.ArgErr() + } + // make the route with the matcher + return h.NewRoute(userMatcherSet, caddyhttp.VarsMiddleware{"root": h.Val()}), nil } // parseFilesystem parses the fs directive. Syntax: diff --git a/caddytest/integration/caddyfile_adapt/rewrite_directive_permutations.txt b/caddytest/integration/caddyfile_adapt/rewrite_directive_permutations.txt new file mode 100644 index 00000000..870e82af --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/rewrite_directive_permutations.txt @@ -0,0 +1,112 @@ +:8080 + +# With explicit wildcard matcher +route { + rewrite * /a +} + +# With path matcher +route { + rewrite /path /b +} + +# With named matcher +route { + @named method GET + rewrite @named /c +} + +# With no matcher, assumed to be wildcard +route { + rewrite /d +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":8080" + ], + "routes": [ + { + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "group": "group0", + "handle": [ + { + "handler": "rewrite", + "uri": "/a" + } + ] + } + ] + }, + { + "handler": "subroute", + "routes": [ + { + "group": "group1", + "handle": [ + { + "handler": "rewrite", + "uri": "/b" + } + ], + "match": [ + { + "path": [ + "/path" + ] + } + ] + } + ] + }, + { + "handler": "subroute", + "routes": [ + { + "group": "group2", + "handle": [ + { + "handler": "rewrite", + "uri": "/c" + } + ], + "match": [ + { + "method": [ + "GET" + ] + } + ] + } + ] + }, + { + "handler": "subroute", + "routes": [ + { + "group": "group3", + "handle": [ + { + "handler": "rewrite", + "uri": "/d" + } + ] + } + ] + } + ] + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/caddytest/integration/caddyfile_adapt/root_directive_permutations.txt b/caddytest/integration/caddyfile_adapt/root_directive_permutations.txt new file mode 100644 index 00000000..b2ef86c4 --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/root_directive_permutations.txt @@ -0,0 +1,108 @@ +:8080 + +# With explicit wildcard matcher +route { + root * /a +} + +# With path matcher +route { + root /path /b +} + +# With named matcher +route { + @named method GET + root @named /c +} + +# With no matcher, assumed to be wildcard +route { + root /d +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":8080" + ], + "routes": [ + { + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "handler": "vars", + "root": "/a" + } + ] + } + ] + }, + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "handler": "vars", + "root": "/b" + } + ], + "match": [ + { + "path": [ + "/path" + ] + } + ] + } + ] + }, + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "handler": "vars", + "root": "/c" + } + ], + "match": [ + { + "method": [ + "GET" + ] + } + ] + } + ] + }, + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "handler": "vars", + "root": "/d" + } + ] + } + ] + } + ] + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/modules/caddyhttp/rewrite/caddyfile.go b/modules/caddyhttp/rewrite/caddyfile.go index a34c1bb0..5d7bd32e 100644 --- a/modules/caddyhttp/rewrite/caddyfile.go +++ b/modules/caddyhttp/rewrite/caddyfile.go @@ -26,7 +26,7 @@ import ( ) func init() { - httpcaddyfile.RegisterHandlerDirective("rewrite", parseCaddyfileRewrite) + httpcaddyfile.RegisterDirective("rewrite", parseCaddyfileRewrite) httpcaddyfile.RegisterHandlerDirective("method", parseCaddyfileMethod) httpcaddyfile.RegisterHandlerDirective("uri", parseCaddyfileURI) httpcaddyfile.RegisterDirective("handle_path", parseCaddyfileHandlePath) @@ -38,7 +38,44 @@ func init() { // // Only URI components which are given in will be set in the resulting URI. // See the docs for the rewrite handler for more information. -func parseCaddyfileRewrite(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { +func parseCaddyfileRewrite(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) { + // consume directive name + if !h.NextArg() { + return nil, h.ArgErr() + } + + // count the tokens to determine what to do + argsCount := h.CountRemainingArgs() + if argsCount == 0 { + return nil, h.Errf("too few arguments; must have at least a rewrite URI") + } + if argsCount > 2 { + return nil, h.Errf("too many arguments; should only be a matcher and a URI") + } + + // with only one arg, assume it's a rewrite URI with no matcher token + if argsCount == 1 { + if !h.NextArg() { + return nil, h.ArgErr() + } + return h.NewRoute(nil, Rewrite{URI: h.Val()}), nil + } + + // parse the matcher token into a matcher set + userMatcherSet, err := h.ExtractMatcherSet() + if err != nil { + return nil, err + } + + // consume directive name, again, because extracting matcher does a reset + if !h.NextArg() { + return nil, h.ArgErr() + } + // advance to the rewrite URI + if !h.NextArg() { + return nil, h.ArgErr() + } + var rewr Rewrite for h.Next() { if !h.NextArg() { @@ -49,7 +86,7 @@ func parseCaddyfileRewrite(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, return nil, h.ArgErr() } } - return rewr, nil + return h.NewRoute(userMatcherSet, Rewrite{URI: h.Val()}), nil } // parseCaddyfileMethod sets up a basic method rewrite handler from Caddyfile tokens. Syntax: