diff options
author | Francis Lavoie <[email protected]> | 2024-10-11 19:41:43 -0400 |
---|---|---|
committer | Francis Lavoie <[email protected]> | 2024-11-04 14:10:51 -0500 |
commit | b3568a8b2c8c9ba42f4534933ba91f569844ac6f (patch) | |
tree | c4168e7e3fc10263eb2c53b703800a562ff56067 | |
parent | 565ce58feeb4786747cc3ea37429f97ddfda4c27 (diff) | |
download | caddy-b3568a8b2c8c9ba42f4534933ba91f569844ac6f.tar.gz caddy-b3568a8b2c8c9ba42f4534933ba91f569844ac6f.zip |
CEL factories can return RequestMatcherWithError
-rw-r--r-- | modules/caddyhttp/celmatcher.go | 109 | ||||
-rw-r--r-- | modules/caddyhttp/fileserver/matcher.go | 6 | ||||
-rw-r--r-- | modules/caddyhttp/ip_matchers.go | 4 | ||||
-rw-r--r-- | modules/caddyhttp/matchers.go | 24 | ||||
-rw-r--r-- | modules/caddyhttp/vars.go | 6 |
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 { |