|
|
@ -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) |
|
|
|