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)