mirror of
https://github.com/caddyserver/caddy.git
synced 2025-02-03 23:09:57 -05:00
cel: Leverage DefaultAdapter to extend CEL's type system
Thanks to @TristonianJones for the tip!
105acfa086 (r38358983)
This commit is contained in:
parent
e30deedcc1
commit
4d9b63d909
1 changed files with 37 additions and 61 deletions
|
@ -53,6 +53,7 @@ type MatchExpression struct {
|
||||||
|
|
||||||
expandedExpr string
|
expandedExpr string
|
||||||
prg cel.Program
|
prg cel.Program
|
||||||
|
ta ref.TypeAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaddyModule returns the Caddy module information.
|
// CaddyModule returns the Caddy module information.
|
||||||
|
@ -79,6 +80,9 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
|
||||||
// light (and possibly naïve) syntactic sugar
|
// light (and possibly naïve) syntactic sugar
|
||||||
m.expandedExpr = placeholderRegexp.ReplaceAllString(m.Expr, placeholderExpansion)
|
m.expandedExpr = placeholderRegexp.ReplaceAllString(m.Expr, placeholderExpansion)
|
||||||
|
|
||||||
|
// our type adapter expands CEL's standard type support
|
||||||
|
m.ta = celTypeAdapter{}
|
||||||
|
|
||||||
// create the CEL environment
|
// create the CEL environment
|
||||||
env, err := cel.NewEnv(
|
env, err := cel.NewEnv(
|
||||||
cel.Declarations(
|
cel.Declarations(
|
||||||
|
@ -88,7 +92,7 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
|
||||||
[]*exprpb.Type{httpRequestObjectType, decls.String},
|
[]*exprpb.Type{httpRequestObjectType, decls.String},
|
||||||
decls.Any)),
|
decls.Any)),
|
||||||
),
|
),
|
||||||
cel.CustomTypeAdapter(celHTTPRequestTypeAdapter{}),
|
cel.CustomTypeAdapter(m.ta),
|
||||||
ext.Strings(),
|
ext.Strings(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -112,7 +116,7 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
|
||||||
cel.Functions(
|
cel.Functions(
|
||||||
&functions.Overload{
|
&functions.Overload{
|
||||||
Operator: placeholderFuncName,
|
Operator: placeholderFuncName,
|
||||||
Binary: caddyPlaceholderFunc,
|
Binary: m.caddyPlaceholderFunc,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -143,14 +147,34 @@ func (m *MatchExpression) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
return nil
|
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.
|
// httpRequestCELType is the type representation of a native HTTP request.
|
||||||
var httpRequestCELType = types.NewTypeValue("http.Request", traits.ReceiverType)
|
var httpRequestCELType = types.NewTypeValue("http.Request", traits.ReceiverType)
|
||||||
|
|
||||||
// cellHTTPRequest wraps an http.Request with
|
// cellHTTPRequest wraps an http.Request with
|
||||||
// methods to satisfy the ref.Val interface.
|
// methods to satisfy the ref.Val interface.
|
||||||
type celHTTPRequest struct {
|
type celHTTPRequest struct{ *http.Request }
|
||||||
*http.Request
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cr celHTTPRequest) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
|
func (cr celHTTPRequest) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
|
||||||
return cr.Request, nil
|
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 (celHTTPRequest) Type() ref.Type { return httpRequestCELType }
|
||||||
func (cr celHTTPRequest) Value() interface{} { return cr }
|
func (cr celHTTPRequest) Value() interface{} { return cr }
|
||||||
|
|
||||||
// celHTTPRequestTypeAdapter can adapt a
|
// celTypeAdapter can adapt our custom types to a CEL value.
|
||||||
// celHTTPRequest to a CEL value.
|
type celTypeAdapter struct{}
|
||||||
type celHTTPRequestTypeAdapter struct{}
|
|
||||||
|
|
||||||
func (celHTTPRequestTypeAdapter) NativeToValue(value interface{}) ref.Val {
|
func (celTypeAdapter) NativeToValue(value interface{}) ref.Val {
|
||||||
if celReq, ok := value.(celHTTPRequest); ok {
|
switch v := value.(type) {
|
||||||
return celReq
|
case celHTTPRequest:
|
||||||
|
return v
|
||||||
|
case error:
|
||||||
|
types.NewErr(v.Error())
|
||||||
}
|
}
|
||||||
return types.DefaultTypeAdapter.NativeToValue(value)
|
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.
|
// The name of the CEL function which accesses Replacer values.
|
||||||
const placeholderFuncName = "caddyPlaceholder"
|
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
|
// Interface guards
|
||||||
var (
|
var (
|
||||||
_ caddy.Provisioner = (*MatchExpression)(nil)
|
_ caddy.Provisioner = (*MatchExpression)(nil)
|
||||||
|
|
Loading…
Add table
Reference in a new issue