summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/celmatcher.go
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2020-04-08 10:44:36 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2020-04-08 10:44:40 -0600
commit4d9b63d9097bd7bac46ecc06c292bf7c521fdcb6 (patch)
tree08719bce5565f24911c31b82267c72fe0774be35 /modules/caddyhttp/celmatcher.go
parente30deedcc1490e1fc73b375a0d3147e625aa7472 (diff)
cel: Leverage DefaultAdapter to extend CEL's type system
Thanks to @TristonianJones for the tip! https://github.com/caddyserver/caddy/commit/105acfa08664c97460a6fe3fb49635618be5bcb2#r38358983
Diffstat (limited to 'modules/caddyhttp/celmatcher.go')
-rw-r--r--modules/caddyhttp/celmatcher.go98
1 files changed, 37 insertions, 61 deletions
diff --git a/modules/caddyhttp/celmatcher.go b/modules/caddyhttp/celmatcher.go
index 84565e4..ddaf418 100644
--- a/modules/caddyhttp/celmatcher.go
+++ b/modules/caddyhttp/celmatcher.go
@@ -53,6 +53,7 @@ type MatchExpression struct {
expandedExpr string
prg cel.Program
+ ta ref.TypeAdapter
}
// CaddyModule returns the Caddy module information.
@@ -79,6 +80,9 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
// light (and possibly naïve) syntactic sugar
m.expandedExpr = placeholderRegexp.ReplaceAllString(m.Expr, placeholderExpansion)
+ // our type adapter expands CEL's standard type support
+ m.ta = celTypeAdapter{}
+
// create the CEL environment
env, err := cel.NewEnv(
cel.Declarations(
@@ -88,7 +92,7 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
[]*exprpb.Type{httpRequestObjectType, decls.String},
decls.Any)),
),
- cel.CustomTypeAdapter(celHTTPRequestTypeAdapter{}),
+ cel.CustomTypeAdapter(m.ta),
ext.Strings(),
)
if err != nil {
@@ -112,7 +116,7 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
cel.Functions(
&functions.Overload{
Operator: placeholderFuncName,
- Binary: caddyPlaceholderFunc,
+ Binary: m.caddyPlaceholderFunc,
},
),
)
@@ -143,14 +147,34 @@ func (m *MatchExpression) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return nil
}
+// caddyPlaceholderFunc implements the custom CEL function that accesses the
+// Replacer on a request and gets values from it.
+func (m MatchExpression) caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
+ celReq, ok := lhs.(celHTTPRequest)
+ if !ok {
+ return types.NewErr(
+ "invalid request of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
+ lhs.Type())
+ }
+ phStr, ok := rhs.(types.String)
+ if !ok {
+ return types.NewErr(
+ "invalid placeholder variable name of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
+ rhs.Type())
+ }
+
+ repl := celReq.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
+ val, _ := repl.Get(string(phStr))
+
+ return m.ta.NativeToValue(val)
+}
+
// httpRequestCELType is the type representation of a native HTTP request.
var httpRequestCELType = types.NewTypeValue("http.Request", traits.ReceiverType)
// cellHTTPRequest wraps an http.Request with
// methods to satisfy the ref.Val interface.
-type celHTTPRequest struct {
- *http.Request
-}
+type celHTTPRequest struct{ *http.Request }
func (cr celHTTPRequest) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
return cr.Request, nil
@@ -167,13 +191,15 @@ func (cr celHTTPRequest) Equal(other ref.Val) ref.Val {
func (celHTTPRequest) Type() ref.Type { return httpRequestCELType }
func (cr celHTTPRequest) Value() interface{} { return cr }
-// celHTTPRequestTypeAdapter can adapt a
-// celHTTPRequest to a CEL value.
-type celHTTPRequestTypeAdapter struct{}
+// celTypeAdapter can adapt our custom types to a CEL value.
+type celTypeAdapter struct{}
-func (celHTTPRequestTypeAdapter) NativeToValue(value interface{}) ref.Val {
- if celReq, ok := value.(celHTTPRequest); ok {
- return celReq
+func (celTypeAdapter) NativeToValue(value interface{}) ref.Val {
+ switch v := value.(type) {
+ case celHTTPRequest:
+ return v
+ case error:
+ types.NewErr(v.Error())
}
return types.DefaultTypeAdapter.NativeToValue(value)
}
@@ -191,56 +217,6 @@ var httpRequestObjectType = decls.NewObjectType("http.Request")
// The name of the CEL function which accesses Replacer values.
const placeholderFuncName = "caddyPlaceholder"
-// caddyPlaceholderFunc implements the custom CEL function that
-// accesses the Replacer on a request and gets values from it.
-func caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
- celReq, ok := lhs.(celHTTPRequest)
- if !ok {
- return types.NewErr(
- "invalid request of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
- lhs.Type())
- }
- phStr, ok := rhs.(types.String)
- if !ok {
- return types.NewErr(
- "invalid placeholder variable name of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
- rhs.Type())
- }
-
- repl := celReq.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
- val, _ := repl.Get(string(phStr))
-
- // TODO: this is... kinda awful and underwhelming, how can we expand CEL's type system more easily?
- switch v := val.(type) {
- case string:
- return types.String(v)
- case fmt.Stringer:
- return types.String(v.String())
- case error:
- return types.NewErr(v.Error())
- case int:
- return types.Int(v)
- case int32:
- return types.Int(v)
- case int64:
- return types.Int(v)
- case uint:
- return types.Int(v)
- case uint32:
- return types.Int(v)
- case uint64:
- return types.Int(v)
- case float32:
- return types.Double(v)
- case float64:
- return types.Double(v)
- case bool:
- return types.Bool(v)
- default:
- return types.String(fmt.Sprintf("%+v", v))
- }
-}
-
// Interface guards
var (
_ caddy.Provisioner = (*MatchExpression)(nil)