@marcel91200 / caddy Mirror · read-only / commits / 4d9b63d9
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

cel: Leverage DefaultAdapter to extend CEL's type system

Thanks to @TristonianJones for the tip! https://github.com/caddyserver/caddy/commit/105acfa08664c97460a6fe3fb49635618be5bcb2#r38358983

Matthew Holt committed Apr 8, 2020 at 10:44 UTC 4d9b63d9097bd7bac46ecc06c292bf7c521fdcb6
1 file changed +37 -61
modules/caddyhttp/celmatcher.go
+37 -61
@@ -53,6 +53,7 @@ type MatchExpression struct {
53
54 expandedExpr string
55 prg cel.Program
56 + ta ref.TypeAdapter
57 }
58
59 // CaddyModule returns the Caddy module information.
@@ -79,6 +80,9 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
80 // light (and possibly naïve) syntactic sugar
81 m.expandedExpr = placeholderRegexp.ReplaceAllString(m.Expr, placeholderExpansion)
82
83 + // our type adapter expands CEL's standard type support
84 + m.ta = celTypeAdapter{}
85 +
86 // create the CEL environment
87 env, err := cel.NewEnv(
88 cel.Declarations(
@@ -88,7 +92,7 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
92 []*exprpb.Type{httpRequestObjectType, decls.String},
93 decls.Any)),
94 ),
91 - cel.CustomTypeAdapter(celHTTPRequestTypeAdapter{}),
95 + cel.CustomTypeAdapter(m.ta),
96 ext.Strings(),
97 )
98 if err != nil {
@@ -112,7 +116,7 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
116 cel.Functions(
117 &functions.Overload{
118 Operator: placeholderFuncName,
115 - Binary: caddyPlaceholderFunc,
119 + Binary: m.caddyPlaceholderFunc,
120 },
121 ),
122 )
@@ -143,14 +147,34 @@ func (m *MatchExpression) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
147 return nil
148 }
149
150 +// caddyPlaceholderFunc implements the custom CEL function that accesses the
151 +// Replacer on a request and gets values from it.
152 +func (m MatchExpression) caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
153 + celReq, ok := lhs.(celHTTPRequest)
154 + if !ok {
155 + return types.NewErr(
156 + "invalid request of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
157 + lhs.Type())
158 + }
159 + phStr, ok := rhs.(types.String)
160 + if !ok {
161 + return types.NewErr(
162 + "invalid placeholder variable name of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
163 + rhs.Type())
164 + }
165 +
166 + repl := celReq.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
167 + val, _ := repl.Get(string(phStr))
168 +
169 + return m.ta.NativeToValue(val)
170 +}
171 +
172 // httpRequestCELType is the type representation of a native HTTP request.
173 var httpRequestCELType = types.NewTypeValue("http.Request", traits.ReceiverType)
174
175 // cellHTTPRequest wraps an http.Request with
176 // methods to satisfy the ref.Val interface.
151 -type celHTTPRequest struct {
152 - *http.Request
153 -}
177 +type celHTTPRequest struct{ *http.Request }
178
179 func (cr celHTTPRequest) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
180 return cr.Request, nil
@@ -167,13 +191,15 @@ func (cr celHTTPRequest) Equal(other ref.Val) ref.Val {
191 func (celHTTPRequest) Type() ref.Type { return httpRequestCELType }
192 func (cr celHTTPRequest) Value() interface{} { return cr }
193
170 -// celHTTPRequestTypeAdapter can adapt a
171 -// celHTTPRequest to a CEL value.
172 -type celHTTPRequestTypeAdapter struct{}
194 +// celTypeAdapter can adapt our custom types to a CEL value.
195 +type celTypeAdapter struct{}
196
174 -func (celHTTPRequestTypeAdapter) NativeToValue(value interface{}) ref.Val {
175 - if celReq, ok := value.(celHTTPRequest); ok {
176 - return celReq
197 +func (celTypeAdapter) NativeToValue(value interface{}) ref.Val {
198 + switch v := value.(type) {
199 + case celHTTPRequest:
200 + return v
201 + case error:
202 + types.NewErr(v.Error())
203 }
204 return types.DefaultTypeAdapter.NativeToValue(value)
205 }
@@ -191,56 +217,6 @@ var httpRequestObjectType = decls.NewObjectType("http.Request")
217 // The name of the CEL function which accesses Replacer values.
218 const placeholderFuncName = "caddyPlaceholder"
219
194 -// caddyPlaceholderFunc implements the custom CEL function that
195 -// accesses the Replacer on a request and gets values from it.
196 -func caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
197 - celReq, ok := lhs.(celHTTPRequest)
198 - if !ok {
199 - return types.NewErr(
200 - "invalid request of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
201 - lhs.Type())
202 - }
203 - phStr, ok := rhs.(types.String)
204 - if !ok {
205 - return types.NewErr(
206 - "invalid placeholder variable name of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
207 - rhs.Type())
208 - }
209 -
210 - repl := celReq.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
211 - val, _ := repl.Get(string(phStr))
212 -
213 - // TODO: this is... kinda awful and underwhelming, how can we expand CEL's type system more easily?
214 - switch v := val.(type) {
215 - case string:
216 - return types.String(v)
217 - case fmt.Stringer:
218 - return types.String(v.String())
219 - case error:
220 - return types.NewErr(v.Error())
221 - case int:
222 - return types.Int(v)
223 - case int32:
224 - return types.Int(v)
225 - case int64:
226 - return types.Int(v)
227 - case uint:
228 - return types.Int(v)
229 - case uint32:
230 - return types.Int(v)
231 - case uint64:
232 - return types.Int(v)
233 - case float32:
234 - return types.Double(v)
235 - case float64:
236 - return types.Double(v)
237 - case bool:
238 - return types.Bool(v)
239 - default:
240 - return types.String(fmt.Sprintf("%+v", v))
241 - }
242 -}
243 -
220 // Interface guards
221 var (
222 _ caddy.Provisioner = (*MatchExpression)(nil)