@marcel91200 / caddy Mirror · read-only
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 11, 01:00 UTC

master
go 489 lines 13.5 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddyhttp
16
17 import (
18 "context"
19 "errors"
20 "fmt"
21 "net/http"
22 "reflect"
23 "strings"
24
25 "cel.dev/cel-go/cel"
26 "cel.dev/cel-go/common/types/ref"
27
28 "github.com/caddyserver/caddy/v2"
29 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
30 )
31
32 var stringSliceType = reflect.TypeFor[[]string]()
33
34 func init() {
35 caddy.RegisterModule(VarsMiddleware{})
36 caddy.RegisterModule(VarsMatcher{})
37 caddy.RegisterModule(MatchVarsRE{})
38 }
39
40 // VarsMiddleware is an HTTP middleware which sets variables to
41 // have values that can be used in the HTTP request handler
42 // chain. The primary way to access variables is with placeholders,
43 // which have the form: `{http.vars.variable_name}`, or with
44 // the `vars` and `vars_regexp` request matchers.
45 //
46 // The key is the variable name, and the value is the value of the
47 // variable. Both the name and value may use or contain placeholders.
48 type VarsMiddleware map[string]any
49
50 // CaddyModule returns the Caddy module information.
51 func (VarsMiddleware) CaddyModule() caddy.ModuleInfo {
52 return caddy.ModuleInfo{
53 ID: "http.handlers.vars",
54 New: func() caddy.Module { return new(VarsMiddleware) },
55 }
56 }
57
58 func (m VarsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handler) error {
59 vars := r.Context().Value(VarsCtxKey).(map[string]any)
60 repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
61 for k, v := range m {
62 keyExpanded := repl.ReplaceAll(k, "")
63 if valStr, ok := v.(string); ok {
64 v = repl.ReplaceAll(valStr, "")
65 }
66 vars[keyExpanded] = v
67
68 // Special case: the user ID is in the replacer, pulled from there
69 // for access logs. Allow users to override it with the vars handler.
70 if keyExpanded == "http.auth.user.id" {
71 repl.Set(keyExpanded, v)
72 }
73 }
74 return next.ServeHTTP(w, r)
75 }
76
77 // UnmarshalCaddyfile implements caddyfile.Unmarshaler. Syntax:
78 //
79 // vars [<name> <val>] {
80 // <name> <val>
81 // ...
82 // }
83 func (m *VarsMiddleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
84 d.Next() // consume directive name
85
86 if *m == nil {
87 *m = make(VarsMiddleware)
88 }
89
90 nextVar := func(headerLine bool) error {
91 if headerLine {
92 // header line is optional
93 if !d.NextArg() {
94 return nil
95 }
96 }
97 varName := d.Val()
98
99 if !d.NextArg() {
100 return d.ArgErr()
101 }
102 varValue := d.ScalarVal()
103
104 (*m)[varName] = varValue
105
106 if d.NextArg() {
107 return d.ArgErr()
108 }
109 return nil
110 }
111
112 if err := nextVar(true); err != nil {
113 return err
114 }
115 for d.NextBlock(0) {
116 if err := nextVar(false); err != nil {
117 return err
118 }
119 }
120
121 return nil
122 }
123
124 // VarsMatcher is an HTTP request matcher which can match
125 // requests based on variables in the context or placeholder
126 // values. The key is the placeholder or name of the variable,
127 // and the values are possible values the variable can be in
128 // order to match (logical OR'ed).
129 //
130 // If the key is surrounded by `{ }` it is assumed to be a
131 // placeholder. Otherwise, it will be considered a variable
132 // name.
133 //
134 // Placeholders in the keys are not expanded, but
135 // placeholders in the values are.
136 type VarsMatcher map[string][]string
137
138 // CaddyModule returns the Caddy module information.
139 func (VarsMatcher) CaddyModule() caddy.ModuleInfo {
140 return caddy.ModuleInfo{
141 ID: "http.matchers.vars",
142 New: func() caddy.Module { return new(VarsMatcher) },
143 }
144 }
145
146 // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
147 func (m *VarsMatcher) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
148 if *m == nil {
149 *m = make(map[string][]string)
150 }
151 // iterate to merge multiple matchers into one
152 for d.Next() {
153 var field string
154 if !d.Args(&field) {
155 return d.Errf("malformed vars matcher: expected field name")
156 }
157 vals := d.RemainingArgs()
158 if len(vals) == 0 {
159 return d.Errf("malformed vars matcher: expected at least one value to match against")
160 }
161 (*m)[field] = append((*m)[field], vals...)
162 if d.NextBlock(0) {
163 return d.Err("malformed vars matcher: blocks are not supported")
164 }
165 }
166 return nil
167 }
168
169 // Match matches a request based on variables in the context,
170 // or placeholders if the key is not a variable.
171 func (m VarsMatcher) Match(r *http.Request) bool {
172 match, _ := m.MatchWithError(r)
173 return match
174 }
175
176 // MatchWithError returns true if r matches m.
177 func (m VarsMatcher) MatchWithError(r *http.Request) (bool, error) {
178 if len(m) == 0 {
179 return true, nil
180 }
181
182 vars := r.Context().Value(VarsCtxKey).(map[string]any)
183 repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
184
185 var matcherValExpanded, varStr string
186 var varValue any
187 for key, vals := range m {
188 if strings.HasPrefix(key, "{") &&
189 strings.HasSuffix(key, "}") &&
190 strings.Count(key, "{") == 1 {
191 varValue, _ = repl.Get(strings.Trim(key, "{}"))
192 } else {
193 varValue = vars[key]
194 }
195
196 switch vv := varValue.(type) {
197 case string:
198 varStr = vv
199 case fmt.Stringer:
200 varStr = vv.String()
201 case error:
202 // propagate the request-body limit marker (a 413 from the
203 // request_body max_size limit when {http.request.body} is
204 // truncated) instead of matching on its error text; unrelated
205 // errors are matched on their text as before
206 if bodyLimit, ok := errors.AsType[RequestBodyLimitError](vv); ok {
207 // wrap in a status-carrying handler error so the server
208 // renders the 413
209 return false, HandlerError{Err: bodyLimit, StatusCode: bodyLimit.StatusCode()}
210 }
211 varStr = vv.Error()
212 case nil:
213 varStr = ""
214 default:
215 varStr = fmt.Sprintf("%v", vv)
216 }
217
218 // Don't expand placeholders in values from literal variable names
219 // (e.g. map outputs) or other placeholders. These values are
220 // already final and must not be re-expanded, as that would allow
221 // user input like {env.SECRET} to be evaluated.
222
223 // see if any of the values given in the matcher match the actual value
224 for _, v := range vals {
225 matcherValExpanded = repl.ReplaceAll(v, "")
226 if varStr == matcherValExpanded {
227 return true, nil
228 }
229 }
230 }
231 return false, nil
232 }
233
234 // CELLibrary produces options that expose this matcher for use in CEL
235 // expression matchers.
236 //
237 // Example:
238 //
239 // expression vars({'{magic_number}': ['3', '5']})
240 // expression vars({'{foo}': 'single_value'})
241 func (VarsMatcher) CELLibrary(_ caddy.Context) (cel.Library, error) {
242 return CELMatcherImpl(
243 "vars",
244 "vars_matcher_request_map",
245 []*cel.Type{CELTypeJSON},
246 func(data ref.Val) (RequestMatcherWithError, error) {
247 mapStrListStr, err := CELValueToMapStrList(data)
248 if err != nil {
249 return nil, err
250 }
251 return VarsMatcher(mapStrListStr), nil
252 },
253 )
254 }
255
256 // MatchVarsRE matches the value of the context variables by a given regular expression.
257 //
258 // Upon a match, it adds placeholders to the request: `{http.regexp.name.capture_group}`
259 // where `name` is the regular expression's name, and `capture_group` is either
260 // the named or positional capture group from the expression itself. If no name
261 // is given, then the placeholder omits the name: `{http.regexp.capture_group}`
262 // (potentially leading to collisions).
263 type MatchVarsRE map[string]*MatchRegexp
264
265 // CaddyModule returns the Caddy module information.
266 func (MatchVarsRE) CaddyModule() caddy.ModuleInfo {
267 return caddy.ModuleInfo{
268 ID: "http.matchers.vars_regexp",
269 New: func() caddy.Module { return new(MatchVarsRE) },
270 }
271 }
272
273 // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
274 func (m *MatchVarsRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
275 if *m == nil {
276 *m = make(map[string]*MatchRegexp)
277 }
278 // iterate to merge multiple matchers into one
279 for d.Next() {
280 var first, second, third string
281 if !d.Args(&first, &second) {
282 return d.ArgErr()
283 }
284
285 var name, field, val string
286 if d.Args(&third) {
287 name = first
288 field = second
289 val = third
290 } else {
291 field = first
292 val = second
293 }
294
295 // Default to the named matcher's name, if no regexp name is provided
296 if name == "" {
297 name = d.GetContextString(caddyfile.MatcherNameCtxKey)
298 }
299
300 (*m)[field] = &MatchRegexp{Pattern: val, Name: name}
301 if d.NextBlock(0) {
302 return d.Err("malformed vars_regexp matcher: blocks are not supported")
303 }
304 }
305 return nil
306 }
307
308 // Provision compiles m's regular expressions.
309 func (m MatchVarsRE) Provision(ctx caddy.Context) error {
310 for _, rm := range m {
311 err := rm.Provision(ctx)
312 if err != nil {
313 return err
314 }
315 }
316 return nil
317 }
318
319 // Match returns true if r matches m.
320 func (m MatchVarsRE) Match(r *http.Request) bool {
321 match, _ := m.MatchWithError(r)
322 return match
323 }
324
325 // MatchWithError returns true if r matches m.
326 func (m MatchVarsRE) MatchWithError(r *http.Request) (bool, error) {
327 vars := r.Context().Value(VarsCtxKey).(map[string]any)
328 repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
329
330 var match bool
331 var varStr string
332 var varValue any
333 for key, val := range m {
334 if strings.HasPrefix(key, "{") &&
335 strings.HasSuffix(key, "}") &&
336 strings.Count(key, "{") == 1 {
337 varValue, _ = repl.Get(strings.Trim(key, "{}"))
338 } else {
339 varValue = vars[key]
340 }
341
342 switch vv := varValue.(type) {
343 case string:
344 varStr = vv
345 case fmt.Stringer:
346 varStr = vv.String()
347 case error:
348 // propagate the request-body limit marker (a 413 from the
349 // request_body max_size limit when {http.request.body} is
350 // truncated) instead of matching on its error text; unrelated
351 // errors are matched on their text as before
352 if bodyLimit, ok := errors.AsType[RequestBodyLimitError](vv); ok {
353 // wrap in a status-carrying handler error so the server
354 // renders the 413
355 return false, HandlerError{Err: bodyLimit, StatusCode: bodyLimit.StatusCode()}
356 }
357 varStr = vv.Error()
358 case nil:
359 varStr = ""
360 default:
361 varStr = fmt.Sprintf("%v", vv)
362 }
363
364 // Don't expand placeholders in values from literal variable names
365 // (e.g. map outputs) or other placeholders. These values are
366 // already final and must not be re-expanded, as that would allow
367 // user input like {env.SECRET} to be evaluated.
368
369 if match = val.Match(varStr, repl); match {
370 return match, nil
371 }
372 }
373 return false, nil
374 }
375
376 // CELLibrary produces options that expose this matcher for use in CEL
377 // expression matchers.
378 //
379 // Example:
380 //
381 // expression vars_regexp('foo', '{magic_number}', '[0-9]+')
382 // expression vars_regexp('{magic_number}', '[0-9]+')
383 func (MatchVarsRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
384 unnamedPattern, err := CELMatcherImpl(
385 "vars_regexp",
386 "vars_regexp_request_string_string",
387 []*cel.Type{cel.StringType, cel.StringType},
388 func(data ref.Val) (RequestMatcherWithError, error) {
389 refStringList := stringSliceType
390 params, err := data.ConvertToNative(refStringList)
391 if err != nil {
392 return nil, err
393 }
394 strParams := params.([]string)
395 matcher := MatchVarsRE{}
396 matcher[strParams[0]] = &MatchRegexp{
397 Pattern: strParams[1],
398 Name: ctx.Value(MatcherNameCtxKey).(string),
399 }
400 err = matcher.Provision(ctx)
401 return matcher, err
402 },
403 )
404 if err != nil {
405 return nil, err
406 }
407 namedPattern, err := CELMatcherImpl(
408 "vars_regexp",
409 "vars_regexp_request_string_string_string",
410 []*cel.Type{cel.StringType, cel.StringType, cel.StringType},
411 func(data ref.Val) (RequestMatcherWithError, error) {
412 refStringList := stringSliceType
413 params, err := data.ConvertToNative(refStringList)
414 if err != nil {
415 return nil, err
416 }
417 strParams := params.([]string)
418 name := strParams[0]
419 if name == "" {
420 name = ctx.Value(MatcherNameCtxKey).(string)
421 }
422 matcher := MatchVarsRE{}
423 matcher[strParams[1]] = &MatchRegexp{
424 Pattern: strParams[2],
425 Name: name,
426 }
427 err = matcher.Provision(ctx)
428 return matcher, err
429 },
430 )
431 if err != nil {
432 return nil, err
433 }
434 envOpts := append(unnamedPattern.CompileOptions(), namedPattern.CompileOptions()...)
435 prgOpts := append(unnamedPattern.ProgramOptions(), namedPattern.ProgramOptions()...)
436 return NewMatcherCELLibrary(envOpts, prgOpts), nil
437 }
438
439 // Validate validates m's regular expressions.
440 func (m MatchVarsRE) Validate() error {
441 for _, rm := range m {
442 err := rm.Validate()
443 if err != nil {
444 return err
445 }
446 }
447 return nil
448 }
449
450 // GetVar gets a value out of the context's variable table by key.
451 // If the key does not exist, the return value will be nil.
452 func GetVar(ctx context.Context, key string) any {
453 varMap, ok := ctx.Value(VarsCtxKey).(map[string]any)
454 if !ok {
455 return nil
456 }
457 return varMap[key]
458 }
459
460 // SetVar sets a value in the context's variable table with
461 // the given key. It overwrites any previous value with the
462 // same key.
463 //
464 // If the value is nil (note: non-nil interface with nil
465 // underlying value does not count) and the key exists in
466 // the table, the key+value will be deleted from the table.
467 func SetVar(ctx context.Context, key string, value any) {
468 varMap, ok := ctx.Value(VarsCtxKey).(map[string]any)
469 if !ok {
470 return
471 }
472 if value == nil {
473 if _, ok := varMap[key]; ok {
474 delete(varMap, key)
475 return
476 }
477 }
478 varMap[key] = value
479 }
480
481 // Interface guards
482 var (
483 _ MiddlewareHandler = (*VarsMiddleware)(nil)
484 _ caddyfile.Unmarshaler = (*VarsMiddleware)(nil)
485 _ RequestMatcherWithError = (*VarsMatcher)(nil)
486 _ caddyfile.Unmarshaler = (*VarsMatcher)(nil)
487 _ RequestMatcherWithError = (*MatchVarsRE)(nil)
488 _ caddyfile.Unmarshaler = (*MatchVarsRE)(nil)
489 )