aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFrancis Lavoie <[email protected]>2024-10-11 19:41:43 -0400
committerFrancis Lavoie <[email protected]>2024-11-04 14:10:51 -0500
commitb3568a8b2c8c9ba42f4534933ba91f569844ac6f (patch)
treec4168e7e3fc10263eb2c53b703800a562ff56067
parent565ce58feeb4786747cc3ea37429f97ddfda4c27 (diff)
downloadcaddy-b3568a8b2c8c9ba42f4534933ba91f569844ac6f.tar.gz
caddy-b3568a8b2c8c9ba42f4534933ba91f569844ac6f.zip
CEL factories can return RequestMatcherWithError
-rw-r--r--modules/caddyhttp/celmatcher.go109
-rw-r--r--modules/caddyhttp/fileserver/matcher.go6
-rw-r--r--modules/caddyhttp/ip_matchers.go4
-rw-r--r--modules/caddyhttp/matchers.go24
-rw-r--r--modules/caddyhttp/vars.go6
5 files changed, 100 insertions, 49 deletions
diff --git a/modules/caddyhttp/celmatcher.go b/modules/caddyhttp/celmatcher.go
index fd6594e3b..cc5086a42 100644
--- a/modules/caddyhttp/celmatcher.go
+++ b/modules/caddyhttp/celmatcher.go
@@ -388,7 +388,7 @@ type CELLibraryProducer interface {
// limited set of function signatures. For strong type validation you may need
// to provide a custom macro which does a more detailed analysis of the CEL
// literal provided to the macro as an argument.
-func CELMatcherImpl(macroName, funcName string, matcherDataTypes []*cel.Type, fac CELMatcherFactory) (cel.Library, error) {
+func CELMatcherImpl(macroName, funcName string, matcherDataTypes []*cel.Type, fac any) (cel.Library, error) {
requestType := cel.ObjectType("http.Request")
var macro parser.Macro
switch len(matcherDataTypes) {
@@ -432,7 +432,11 @@ func CELMatcherImpl(macroName, funcName string, matcherDataTypes []*cel.Type, fa
}
// CELMatcherFactory converts a constant CEL value into a RequestMatcher.
-type CELMatcherFactory func(data ref.Val) (RequestMatcher, error)
+// DEPRECATED: Use CELMatcherWithErrorFactory instead.
+type CELMatcherFactory = func(data ref.Val) (RequestMatcher, error)
+
+// CELMatcherWithErrorFactory converts a constant CEL value into a RequestMatcherWithError.
+type CELMatcherWithErrorFactory = func(data ref.Val) (RequestMatcherWithError, error)
// matcherCELLibrary is a simplistic configurable cel.Library implementation.
type matcherCELLibrary struct {
@@ -460,7 +464,7 @@ func (lib *matcherCELLibrary) ProgramOptions() []cel.ProgramOption {
// that takes a single argument, and optimizes the implementation to precompile
// the matcher and return a function that references the precompiled and
// provisioned matcher.
-func CELMatcherDecorator(funcName string, fac CELMatcherFactory) interpreter.InterpretableDecorator {
+func CELMatcherDecorator(funcName string, fac any) interpreter.InterpretableDecorator {
return func(i interpreter.Interpretable) (interpreter.Interpretable, error) {
call, ok := i.(interpreter.InterpretableCall)
if !ok {
@@ -489,49 +493,92 @@ func CELMatcherDecorator(funcName string, fac CELMatcherFactory) interpreter.Int
// and matcher provisioning should be handled at dynamically.
return i, nil
}
- matcher, err := fac(matcherData.Value())
- if err != nil {
- return nil, err
- }
- return interpreter.NewCall(
- i.ID(), funcName, funcName+"_opt",
- []interpreter.Interpretable{reqAttr},
- func(args ...ref.Val) ref.Val {
- // The request value, guaranteed to be of type celHTTPRequest
- celReq := args[0]
- // If needed this call could be changed to convert the value
- // to a *http.Request using CEL's ConvertToNative method.
- httpReq := celReq.Value().(celHTTPRequest)
- if m, ok := matcher.(RequestMatcherWithError); ok {
- match, err := m.MatchWithError(httpReq.Request)
+
+ if factory, ok := fac.(CELMatcherWithErrorFactory); ok {
+ matcher, err := factory(matcherData.Value())
+ if err != nil {
+ return nil, err
+ }
+ return interpreter.NewCall(
+ i.ID(), funcName, funcName+"_opt",
+ []interpreter.Interpretable{reqAttr},
+ func(args ...ref.Val) ref.Val {
+ // The request value, guaranteed to be of type celHTTPRequest
+ celReq := args[0]
+ // If needed this call could be changed to convert the value
+ // to a *http.Request using CEL's ConvertToNative method.
+ httpReq := celReq.Value().(celHTTPRequest)
+ match, err := matcher.MatchWithError(httpReq.Request)
if err != nil {
return types.WrapErr(err)
}
return types.Bool(match)
- }
- return types.Bool(matcher.Match(httpReq.Request))
- },
- ), nil
+ },
+ ), nil
+ }
+
+ if factory, ok := fac.(CELMatcherFactory); ok {
+ matcher, err := factory(matcherData.Value())
+ if err != nil {
+ return nil, err
+ }
+ return interpreter.NewCall(
+ i.ID(), funcName, funcName+"_opt",
+ []interpreter.Interpretable{reqAttr},
+ func(args ...ref.Val) ref.Val {
+ // The request value, guaranteed to be of type celHTTPRequest
+ celReq := args[0]
+ // If needed this call could be changed to convert the value
+ // to a *http.Request using CEL's ConvertToNative method.
+ httpReq := celReq.Value().(celHTTPRequest)
+ if m, ok := matcher.(RequestMatcherWithError); ok {
+ match, err := m.MatchWithError(httpReq.Request)
+ if err != nil {
+ return types.WrapErr(err)
+ }
+ return types.Bool(match)
+ }
+ return types.Bool(matcher.Match(httpReq.Request))
+ },
+ ), nil
+ }
+
+ return nil, fmt.Errorf("invalid matcher factory, must be CELMatcherFactory or CELMatcherWithErrorFactory: %T", fac)
}
}
// CELMatcherRuntimeFunction creates a function binding for when the input to the matcher
// is dynamically resolved rather than a set of static constant values.
-func CELMatcherRuntimeFunction(funcName string, fac CELMatcherFactory) functions.BinaryOp {
+func CELMatcherRuntimeFunction(funcName string, fac any) functions.BinaryOp {
return func(celReq, matcherData ref.Val) ref.Val {
- matcher, err := fac(matcherData)
- if err != nil {
- return types.WrapErr(err)
- }
- httpReq := celReq.Value().(celHTTPRequest)
- if m, ok := matcher.(RequestMatcherWithError); ok {
- match, err := m.MatchWithError(httpReq.Request)
+ if factory, ok := fac.(CELMatcherWithErrorFactory); ok {
+ matcher, err := factory(matcherData)
+ if err != nil {
+ return types.WrapErr(err)
+ }
+ httpReq := celReq.Value().(celHTTPRequest)
+ match, err := matcher.MatchWithError(httpReq.Request)
if err != nil {
return types.WrapErr(err)
}
return types.Bool(match)
}
- return types.Bool(matcher.Match(httpReq.Request))
+ if factory, ok := fac.(CELMatcherFactory); ok {
+ matcher, err := factory(matcherData)
+ if err != nil {
+ return types.WrapErr(err)
+ }
+ httpReq := celReq.Value().(celHTTPRequest)
+ if m, ok := matcher.(RequestMatcherWithError); ok {
+ match, err := m.MatchWithError(httpReq.Request)
+ if err != nil {
+ return types.WrapErr(err)
+ }
+ return types.Bool(match)
+ }
+ return types.Bool(matcher.Match(httpReq.Request))
+ }
+ return types.NewErr("CELMatcherRuntimeFunction invalid matcher factory: %T", fac)
}
}
diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go
index 0a5eece16..834ee2540 100644
--- a/modules/caddyhttp/fileserver/matcher.go
+++ b/modules/caddyhttp/fileserver/matcher.go
@@ -711,7 +711,7 @@ const (
// Interface guards
var (
- _ caddy.Validator = (*MatchFile)(nil)
- _ caddyhttp.RequestMatcher = (*MatchFile)(nil)
- _ caddyhttp.CELLibraryProducer = (*MatchFile)(nil)
+ _ caddy.Validator = (*MatchFile)(nil)
+ _ caddyhttp.RequestMatcherWithError = (*MatchFile)(nil)
+ _ caddyhttp.CELLibraryProducer = (*MatchFile)(nil)
)
diff --git a/modules/caddyhttp/ip_matchers.go b/modules/caddyhttp/ip_matchers.go
index 9205f53fa..22bd1bb2a 100644
--- a/modules/caddyhttp/ip_matchers.go
+++ b/modules/caddyhttp/ip_matchers.go
@@ -108,7 +108,7 @@ func (MatchRemoteIP) CELLibrary(ctx caddy.Context) (cel.Library, error) {
// internal data type of the MatchPath value.
[]*cel.Type{cel.ListType(cel.StringType)},
// function to convert a constant list of strings to a MatchPath instance.
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
strList, err := data.ConvertToNative(refStringList)
if err != nil {
@@ -218,7 +218,7 @@ func (MatchClientIP) CELLibrary(ctx caddy.Context) (cel.Library, error) {
// internal data type of the MatchPath value.
[]*cel.Type{cel.ListType(cel.StringType)},
// function to convert a constant list of strings to a MatchPath instance.
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
strList, err := data.ConvertToNative(refStringList)
if err != nil {
diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go
index 2197faa70..a8702d67b 100644
--- a/modules/caddyhttp/matchers.go
+++ b/modules/caddyhttp/matchers.go
@@ -372,7 +372,7 @@ func (MatchHost) CELLibrary(ctx caddy.Context) (cel.Library, error) {
"host",
"host_match_request_list",
[]*cel.Type{cel.ListType(cel.StringType)},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
strList, err := data.ConvertToNative(refStringList)
if err != nil {
@@ -654,7 +654,7 @@ func (MatchPath) CELLibrary(ctx caddy.Context) (cel.Library, error) {
// internal data type of the MatchPath value.
[]*cel.Type{cel.ListType(cel.StringType)},
// function to convert a constant list of strings to a MatchPath instance.
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
strList, err := data.ConvertToNative(refStringList)
if err != nil {
@@ -716,7 +716,7 @@ func (MatchPathRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
"path_regexp",
"path_regexp_request_string",
[]*cel.Type{cel.StringType},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
pattern := data.(types.String)
matcher := MatchPathRE{MatchRegexp{
Name: ctx.Value(MatcherNameCtxKey).(string),
@@ -733,7 +733,7 @@ func (MatchPathRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
"path_regexp",
"path_regexp_request_string_string",
[]*cel.Type{cel.StringType, cel.StringType},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
params, err := data.ConvertToNative(refStringList)
if err != nil {
@@ -802,7 +802,7 @@ func (MatchMethod) CELLibrary(_ caddy.Context) (cel.Library, error) {
"method",
"method_request_list",
[]*cel.Type{cel.ListType(cel.StringType)},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
strList, err := data.ConvertToNative(refStringList)
if err != nil {
@@ -909,7 +909,7 @@ func (MatchQuery) CELLibrary(_ caddy.Context) (cel.Library, error) {
"query",
"query_matcher_request_map",
[]*cel.Type{CELTypeJSON},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
mapStrListStr, err := CELValueToMapStrList(data)
if err != nil {
return nil, err
@@ -993,7 +993,7 @@ func (MatchHeader) CELLibrary(_ caddy.Context) (cel.Library, error) {
"header",
"header_matcher_request_map",
[]*cel.Type{CELTypeJSON},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
mapStrListStr, err := CELValueToMapStrList(data)
if err != nil {
return nil, err
@@ -1169,7 +1169,7 @@ func (MatchHeaderRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
"header_regexp",
"header_regexp_request_string_string",
[]*cel.Type{cel.StringType, cel.StringType},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
params, err := data.ConvertToNative(refStringList)
if err != nil {
@@ -1192,7 +1192,7 @@ func (MatchHeaderRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
"header_regexp",
"header_regexp_request_string_string_string",
[]*cel.Type{cel.StringType, cel.StringType, cel.StringType},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
params, err := data.ConvertToNative(refStringList)
if err != nil {
@@ -1287,7 +1287,7 @@ func (MatchProtocol) CELLibrary(_ caddy.Context) (cel.Library, error) {
"protocol",
"protocol_request_string",
[]*cel.Type{cel.StringType},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
protocolStr, ok := data.(types.String)
if !ok {
return nil, errors.New("protocol argument was not a string")
@@ -1603,6 +1603,10 @@ const regexpPlaceholderPrefix = "http.regexp"
// holds an optional error emitted from a request matcher,
// to short-circuit the handler chain, since matchers cannot
// return errors via the RequestMatcher interface.
+//
+// DEPRECATED: Matchers should implement RequestMatcherWithError
+// which can return an error directly, instead of smuggling it
+// through the vars map.
const MatcherErrorVarKey = "matchers.error"
// Interface guards
diff --git a/modules/caddyhttp/vars.go b/modules/caddyhttp/vars.go
index f884c0993..7ab891fc0 100644
--- a/modules/caddyhttp/vars.go
+++ b/modules/caddyhttp/vars.go
@@ -225,7 +225,7 @@ func (VarsMatcher) CELLibrary(_ caddy.Context) (cel.Library, error) {
"vars",
"vars_matcher_request_map",
[]*cel.Type{CELTypeJSON},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
mapStrListStr, err := CELValueToMapStrList(data)
if err != nil {
return nil, err
@@ -352,7 +352,7 @@ func (MatchVarsRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
"vars_regexp",
"vars_regexp_request_string_string",
[]*cel.Type{cel.StringType, cel.StringType},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
params, err := data.ConvertToNative(refStringList)
if err != nil {
@@ -375,7 +375,7 @@ func (MatchVarsRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
"vars_regexp",
"vars_regexp_request_string_string_string",
[]*cel.Type{cel.StringType, cel.StringType, cel.StringType},
- func(data ref.Val) (RequestMatcher, error) {
+ func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := reflect.TypeOf([]string{})
params, err := data.ConvertToNative(refStringList)
if err != nil {