diff options
author | Francis Lavoie <[email protected]> | 2024-03-03 05:14:38 -0500 |
---|---|---|
committer | Francis Lavoie <[email protected]> | 2024-04-17 11:57:30 -0400 |
commit | 9b757ce862fcaf5535c07ef67c233dfc358316ef (patch) | |
tree | 438d1313ea339d19e554b64b34052ea8361f0b12 | |
parent | 244ef288a9fb81366c65976a14d2d5642e9ed24e (diff) | |
download | caddy-9b757ce862fcaf5535c07ef67c233dfc358316ef.tar.gz caddy-9b757ce862fcaf5535c07ef67c233dfc358316ef.zip |
Pass down matcher name through expression matcher
-rw-r--r-- | caddytest/integration/caddyfile_adapt/expression_quotes.caddyfiletest | 10 | ||||
-rw-r--r-- | caddytest/integration/caddyfile_adapt/matcher_syntax.caddyfiletest | 5 | ||||
-rw-r--r-- | context.go | 12 | ||||
-rw-r--r-- | modules/caddyhttp/celmatcher.go | 47 | ||||
-rw-r--r-- | modules/caddyhttp/celmatcher_test.go | 8 | ||||
-rw-r--r-- | modules/caddyhttp/fileserver/matcher_test.go | 4 | ||||
-rw-r--r-- | modules/caddyhttp/matchers.go | 28 |
7 files changed, 101 insertions, 13 deletions
diff --git a/caddytest/integration/caddyfile_adapt/expression_quotes.caddyfiletest b/caddytest/integration/caddyfile_adapt/expression_quotes.caddyfiletest index f5f8983eb..e0f5bd715 100644 --- a/caddytest/integration/caddyfile_adapt/expression_quotes.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/expression_quotes.caddyfiletest @@ -84,7 +84,10 @@ abort @e ], "match": [ { - "expression": "{http.error.status_code} == 403" + "expression": { + "expr": "{http.error.status_code} == 403", + "name": "d" + } } ] }, @@ -97,7 +100,10 @@ abort @e ], "match": [ { - "expression": "{http.error.status_code} == 404" + "expression": { + "expr": "{http.error.status_code} == 404", + "name": "e" + } } ] } diff --git a/caddytest/integration/caddyfile_adapt/matcher_syntax.caddyfiletest b/caddytest/integration/caddyfile_adapt/matcher_syntax.caddyfiletest index a6b3479a7..0ccee395c 100644 --- a/caddytest/integration/caddyfile_adapt/matcher_syntax.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/matcher_syntax.caddyfiletest @@ -162,7 +162,10 @@ { "match": [ { - "expression": "path('/foo*') \u0026\u0026 method('GET')" + "expression": { + "expr": "path('/foo*') \u0026\u0026 method('GET')", + "name": "matcher7" + } } ], "handle": [ diff --git a/context.go b/context.go index d73af7702..4307dda88 100644 --- a/context.go +++ b/context.go @@ -556,3 +556,15 @@ func (ctx Context) Module() Module { } return ctx.ancestry[len(ctx.ancestry)-1] } + +// WithValue returns a new context with the given key-value pair. +func (ctx *Context) WithValue(key, value any) Context { + return Context{ + Context: context.WithValue(ctx.Context, key, value), + moduleInstances: ctx.moduleInstances, + cfg: ctx.cfg, + ancestry: ctx.ancestry, + cleanupFuncs: ctx.cleanupFuncs, + exitFuncs: ctx.exitFuncs, + } +} diff --git a/modules/caddyhttp/celmatcher.go b/modules/caddyhttp/celmatcher.go index e1f15e869..d4016478e 100644 --- a/modules/caddyhttp/celmatcher.go +++ b/modules/caddyhttp/celmatcher.go @@ -62,7 +62,12 @@ type MatchExpression struct { // The CEL expression to evaluate. Any Caddy placeholders // will be expanded and situated into proper CEL function // calls before evaluating. - Expr string + Expr string `json:"expr,omitempty"` + + // Name is an optional name for this matcher. + // This is used to populate the name for regexp + // matchers that appear in the expression. + Name string `json:"name,omitempty"` expandedExpr string prg cel.Program @@ -81,12 +86,36 @@ func (MatchExpression) CaddyModule() caddy.ModuleInfo { // MarshalJSON marshals m's expression. func (m MatchExpression) MarshalJSON() ([]byte, error) { - return json.Marshal(m.Expr) + // if the name is empty, then we can marshal just the expression string + if m.Name == "" { + return json.Marshal(m.Expr) + } + // otherwise, we need to marshal the full object, using an + // anonymous struct to avoid infinite recursion + return json.Marshal(struct { + Expr string `json:"expr"` + Name string `json:"name"` + }{ + Expr: m.Expr, + Name: m.Name, + }) } // UnmarshalJSON unmarshals m's expression. func (m *MatchExpression) UnmarshalJSON(data []byte) error { - return json.Unmarshal(data, &m.Expr) + // if the data is a string, then it's just the expression + if data[0] == '"' { + return json.Unmarshal(data, &m.Expr) + } + // otherwise, it's a full object, so unmarshal it, + // using an temp map to avoid infinite recursion + var tmpJson map[string]any + err := json.Unmarshal(data, &tmpJson) + *m = MatchExpression{ + Expr: tmpJson["expr"].(string), + Name: tmpJson["name"].(string), + } + return err } // Provision sets ups m. @@ -109,6 +138,11 @@ func (m *MatchExpression) Provision(ctx caddy.Context) error { matcherLibProducers = append(matcherLibProducers, p) } } + + // add the matcher name to the context so that the matcher name + // can be used by regexp matchers being provisioned + ctx = ctx.WithValue(MatcherNameCtxKey, m.Name) + // Assemble the compilation and program options from the different library // producers into a single cel.Library implementation. matcherEnvOpts := []cel.EnvOption{} @@ -197,6 +231,11 @@ func (m *MatchExpression) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { // quoted string; commonly quotes are used in Caddyfile to // define the expression m.Expr = d.Val() + + // use the named matcher's name, to fill regexp + // matchers names by default + m.Name = d.GetContextString(caddyfile.MatcherNameCtxKey) + return nil } @@ -673,6 +712,8 @@ var httpRequestObjectType = cel.ObjectType("http.Request") // The name of the CEL function which accesses Replacer values. const placeholderFuncName = "caddyPlaceholder" +const MatcherNameCtxKey = "matcher_name" + // Interface guards var ( _ caddy.Provisioner = (*MatchExpression)(nil) diff --git a/modules/caddyhttp/celmatcher_test.go b/modules/caddyhttp/celmatcher_test.go index e67b87c77..25fdcf45e 100644 --- a/modules/caddyhttp/celmatcher_test.go +++ b/modules/caddyhttp/celmatcher_test.go @@ -380,7 +380,9 @@ func TestMatchExpressionMatch(t *testing.T) { for _, tst := range matcherTests { tc := tst t.Run(tc.name, func(t *testing.T) { - err := tc.expression.Provision(caddy.Context{}) + caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()}) + defer cancel() + err := tc.expression.Provision(caddyCtx) if err != nil { if !tc.wantErr { t.Errorf("MatchExpression.Provision() error = %v, wantErr %v", err, tc.wantErr) @@ -482,7 +484,9 @@ func TestMatchExpressionProvision(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := tt.expression.Provision(caddy.Context{}); (err != nil) != tt.wantErr { + ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()}) + defer cancel() + if err := tt.expression.Provision(ctx); (err != nil) != tt.wantErr { t.Errorf("MatchExpression.Provision() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/modules/caddyhttp/fileserver/matcher_test.go b/modules/caddyhttp/fileserver/matcher_test.go index 5ffb63b6a..527c16bd1 100644 --- a/modules/caddyhttp/fileserver/matcher_test.go +++ b/modules/caddyhttp/fileserver/matcher_test.go @@ -360,7 +360,9 @@ func TestMatchExpressionMatch(t *testing.T) { for _, tst := range expressionTests { tc := tst t.Run(tc.name, func(t *testing.T) { - err := tc.expression.Provision(caddy.Context{}) + caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()}) + defer cancel() + err := tc.expression.Provision(caddyCtx) if err != nil { if !tc.wantErr { t.Errorf("MatchExpression.Provision() error = %v, wantErr %v", err, tc.wantErr) diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index 8aca605ec..23dc6e8a8 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -675,7 +675,10 @@ func (MatchPathRE) CELLibrary(ctx caddy.Context) (cel.Library, error) { []*cel.Type{cel.StringType}, func(data ref.Val) (RequestMatcher, error) { pattern := data.(types.String) - matcher := MatchPathRE{MatchRegexp{Pattern: string(pattern)}} + matcher := MatchPathRE{MatchRegexp{ + Name: ctx.Value(MatcherNameCtxKey).(string), + Pattern: string(pattern), + }} err := matcher.Provision(ctx) return matcher, err }, @@ -694,7 +697,14 @@ func (MatchPathRE) CELLibrary(ctx caddy.Context) (cel.Library, error) { return nil, err } strParams := params.([]string) - matcher := MatchPathRE{MatchRegexp{Name: strParams[0], Pattern: strParams[1]}} + name := strParams[0] + if name == "" { + name = ctx.Value(MatcherNameCtxKey).(string) + } + matcher := MatchPathRE{MatchRegexp{ + Name: name, + Pattern: strParams[1], + }} err = matcher.Provision(ctx) return matcher, err }, @@ -1104,7 +1114,10 @@ func (MatchHeaderRE) CELLibrary(ctx caddy.Context) (cel.Library, error) { } strParams := params.([]string) matcher := MatchHeaderRE{} - matcher[strParams[0]] = &MatchRegexp{Pattern: strParams[1], Name: ""} + matcher[strParams[0]] = &MatchRegexp{ + Pattern: strParams[1], + Name: ctx.Value(MatcherNameCtxKey).(string), + } err = matcher.Provision(ctx) return matcher, err }, @@ -1123,8 +1136,15 @@ func (MatchHeaderRE) CELLibrary(ctx caddy.Context) (cel.Library, error) { return nil, err } strParams := params.([]string) + name := strParams[0] + if name == "" { + name = ctx.Value(MatcherNameCtxKey).(string) + } matcher := MatchHeaderRE{} - matcher[strParams[1]] = &MatchRegexp{Pattern: strParams[2], Name: strParams[0]} + matcher[strParams[1]] = &MatchRegexp{ + Pattern: strParams[2], + Name: name, + } err = matcher.Provision(ctx) return matcher, err }, |