diff options
| author | Matthew Holt <mholt@users.noreply.github.com> | 2020-04-08 10:44:36 -0600 | 
|---|---|---|
| committer | Matthew Holt <mholt@users.noreply.github.com> | 2020-04-08 10:44:40 -0600 | 
| commit | 4d9b63d9097bd7bac46ecc06c292bf7c521fdcb6 (patch) | |
| tree | 08719bce5565f24911c31b82267c72fe0774be35 /modules | |
| parent | e30deedcc1490e1fc73b375a0d3147e625aa7472 (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')
| -rw-r--r-- | modules/caddyhttp/celmatcher.go | 98 | 
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) | 
