summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancis Lavoie <lavofr@gmail.com>2022-09-01 23:12:37 -0400
committerGitHub <noreply@github.com>2022-09-01 23:12:37 -0400
commit7d5108d132d3bccfd8d83bc3fc2dbc46afde20ae (patch)
treef476b207d820a4dcf64ab9bca1c92af12a384da2
parent7c35bfa57cb402e82b28118b69c265df3e8c94fd (diff)
httpcaddyfile: Add shortcut for expression matchers (#4976)
-rw-r--r--caddyconfig/caddyfile/lexer.go4
-rw-r--r--caddyconfig/httpcaddyfile/httptype.go47
-rw-r--r--caddytest/integration/caddyfile_adapt/matcher_syntax.txt32
3 files changed, 65 insertions, 18 deletions
diff --git a/caddyconfig/caddyfile/lexer.go b/caddyconfig/caddyfile/lexer.go
index 4a23524..5605a6a 100644
--- a/caddyconfig/caddyfile/lexer.go
+++ b/caddyconfig/caddyfile/lexer.go
@@ -191,3 +191,7 @@ func Tokenize(input []byte, filename string) ([]Token, error) {
}
return tokens, nil
}
+
+func (t Token) Quoted() bool {
+ return t.wasQuoted > 0
+}
diff --git a/caddyconfig/httpcaddyfile/httptype.go b/caddyconfig/httpcaddyfile/httptype.go
index 8fef131..2182a5b 100644
--- a/caddyconfig/httpcaddyfile/httptype.go
+++ b/caddyconfig/httpcaddyfile/httptype.go
@@ -1201,6 +1201,7 @@ func (st *ServerType) compileEncodedMatcherSets(sblock serverBlock) ([]caddy.Mod
func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.ModuleMap) error {
for d.Next() {
+ // this is the "name" for "named matchers"
definitionName := d.Val()
if _, ok := matchers[definitionName]; ok {
@@ -1208,16 +1209,9 @@ func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.M
}
matchers[definitionName] = make(caddy.ModuleMap)
- // in case there are multiple instances of the same matcher, concatenate
- // their tokens (we expect that UnmarshalCaddyfile should be able to
- // handle more than one segment); otherwise, we'd overwrite other
- // instances of the matcher in this set
- tokensByMatcherName := make(map[string][]caddyfile.Token)
- for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); {
- matcherName := d.Val()
- tokensByMatcherName[matcherName] = append(tokensByMatcherName[matcherName], d.NextSegment()...)
- }
- for matcherName, tokens := range tokensByMatcherName {
+ // given a matcher name and the tokens following it, parse
+ // the tokens as a matcher module and record it
+ makeMatcher := func(matcherName string, tokens []caddyfile.Token) error {
mod, err := caddy.GetModule("http.matchers." + matcherName)
if err != nil {
return fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
@@ -1235,6 +1229,39 @@ func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.M
return fmt.Errorf("matcher module '%s' is not a request matcher", matcherName)
}
matchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)
+ return nil
+ }
+
+ // if the next token is quoted, we can assume it's not a matcher name
+ // and that it's probably an 'expression' matcher
+ if d.NextArg() {
+ if d.Token().Quoted() {
+ err := makeMatcher("expression", []caddyfile.Token{d.Token()})
+ if err != nil {
+ return err
+ }
+ continue
+ }
+
+ // if it wasn't quoted, then we need to rewind after calling
+ // d.NextArg() so the below properly grabs the matcher name
+ d.Prev()
+ }
+
+ // in case there are multiple instances of the same matcher, concatenate
+ // their tokens (we expect that UnmarshalCaddyfile should be able to
+ // handle more than one segment); otherwise, we'd overwrite other
+ // instances of the matcher in this set
+ tokensByMatcherName := make(map[string][]caddyfile.Token)
+ for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); {
+ matcherName := d.Val()
+ tokensByMatcherName[matcherName] = append(tokensByMatcherName[matcherName], d.NextSegment()...)
+ }
+ for matcherName, tokens := range tokensByMatcherName {
+ err := makeMatcher(matcherName, tokens)
+ if err != nil {
+ return err
+ }
}
}
return nil
diff --git a/caddytest/integration/caddyfile_adapt/matcher_syntax.txt b/caddytest/integration/caddyfile_adapt/matcher_syntax.txt
index a3e0a50..fb3dfb6 100644
--- a/caddytest/integration/caddyfile_adapt/matcher_syntax.txt
+++ b/caddytest/integration/caddyfile_adapt/matcher_syntax.txt
@@ -19,27 +19,30 @@
@matcher6 vars_regexp "{http.request.uri}" `\.([a-f0-9]{6})\.(css|js)$`
respond @matcher6 "from vars_regexp matcher without name"
- @matcher7 {
+ @matcher7 `path('/foo*') && method('GET')`
+ respond @matcher7 "inline expression matcher shortcut"
+
+ @matcher8 {
header Foo bar
header Foo foobar
header Bar foo
}
- respond @matcher7 "header matcher merging values of the same field"
+ respond @matcher8 "header matcher merging values of the same field"
- @matcher8 {
+ @matcher9 {
query foo=bar foo=baz bar=foo
query bar=baz
}
- respond @matcher8 "query matcher merging pairs with the same keys"
+ respond @matcher9 "query matcher merging pairs with the same keys"
- @matcher9 {
+ @matcher10 {
header !Foo
header Bar foo
}
- respond @matcher9 "header matcher with null field matcher"
+ respond @matcher10 "header matcher with null field matcher"
- @matcher10 remote_ip private_ranges
- respond @matcher10 "remote_ip matcher with private ranges"
+ @matcher11 remote_ip private_ranges
+ respond @matcher11 "remote_ip matcher with private ranges"
}
----------
{
@@ -155,6 +158,19 @@
{
"match": [
{
+ "expression": "path('/foo*') \u0026\u0026 method('GET')"
+ }
+ ],
+ "handle": [
+ {
+ "body": "inline expression matcher shortcut",
+ "handler": "static_response"
+ }
+ ]
+ },
+ {
+ "match": [
+ {
"header": {
"Bar": [
"foo"