@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 476 lines 15.7 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 "encoding/json"
19 "fmt"
20 "net/http"
21 "slices"
22 "strings"
23
24 "github.com/caddyserver/caddy/v2"
25 )
26
27 // Route consists of a set of rules for matching HTTP requests,
28 // a list of handlers to execute, and optional flow control
29 // parameters which customize the handling of HTTP requests
30 // in a highly flexible and performant manner.
31 type Route struct {
32 // Group is an optional name for a group to which this
33 // route belongs. Grouping a route makes it mutually
34 // exclusive with others in its group; if a route belongs
35 // to a group, only the first matching route in that group
36 // will be executed.
37 Group string `json:"group,omitempty"`
38
39 // The matcher sets which will be used to qualify this
40 // route for a request (essentially the "if" statement
41 // of this route). Each matcher set is OR'ed, but matchers
42 // within a set are AND'ed together.
43 MatcherSetsRaw RawMatcherSets `json:"match,omitempty" caddy:"namespace=http.matchers"`
44
45 // The list of handlers for this route. Upon matching a request, they are chained
46 // together in a middleware fashion: requests flow from the first handler to the last
47 // (top of the list to the bottom), with the possibility that any handler could stop
48 // the chain and/or return an error. Responses flow back through the chain (bottom of
49 // the list to the top) as they are written out to the client.
50 //
51 // Not all handlers call the next handler in the chain. For example, the reverse_proxy
52 // handler always sends a request upstream or returns an error. Thus, configuring
53 // handlers after reverse_proxy in the same route is illogical, since they would never
54 // be executed. You will want to put handlers which originate the response at the very
55 // end of your route(s). The documentation for a module should state whether it invokes
56 // the next handler, but sometimes it is common sense.
57 //
58 // Some handlers manipulate the response. Remember that requests flow down the list, and
59 // responses flow up the list.
60 //
61 // For example, if you wanted to use both `templates` and `encode` handlers, you would
62 // need to put `templates` after `encode` in your route, because responses flow up.
63 // Thus, `templates` will be able to parse and execute the plain-text response as a
64 // template, and then return it up to the `encode` handler which will then compress it
65 // into a binary format.
66 //
67 // If `templates` came before `encode`, then `encode` would write a compressed,
68 // binary-encoded response to `templates` which would not be able to parse the response
69 // properly.
70 //
71 // The correct order, then, is this:
72 //
73 // [
74 // {"handler": "encode"},
75 // {"handler": "templates"},
76 // {"handler": "file_server"}
77 // ]
78 //
79 // The request flows ⬇️ DOWN (`encode` -> `templates` -> `file_server`).
80 //
81 // 1. First, `encode` will choose how to `encode` the response and wrap the response.
82 // 2. Then, `templates` will wrap the response with a buffer.
83 // 3. Finally, `file_server` will originate the content from a file.
84 //
85 // The response flows ⬆️ UP (`file_server` -> `templates` -> `encode`):
86 //
87 // 1. First, `file_server` will write the file to the response.
88 // 2. That write will be buffered and then executed by `templates`.
89 // 3. Lastly, the write from `templates` will flow into `encode` which will compress the stream.
90 //
91 // If you think of routes in this way, it will be easy and even fun to solve the puzzle of writing correct routes.
92 HandlersRaw []json.RawMessage `json:"handle,omitempty" caddy:"namespace=http.handlers inline_key=handler"`
93
94 // If true, no more routes will be executed after this one.
95 Terminal bool `json:"terminal,omitempty"`
96
97 // decoded values
98 MatcherSets MatcherSets `json:"-"`
99 Handlers []MiddlewareHandler `json:"-"`
100
101 middleware []Middleware
102 metrics *Metrics
103 metricsCtx caddy.Context
104 handlerName string
105 }
106
107 // Empty returns true if the route has all zero/default values.
108 func (r Route) Empty() bool {
109 return len(r.MatcherSetsRaw) == 0 &&
110 len(r.MatcherSets) == 0 &&
111 len(r.HandlersRaw) == 0 &&
112 len(r.Handlers) == 0 &&
113 !r.Terminal &&
114 r.Group == ""
115 }
116
117 func (r Route) String() string {
118 var handlersRaw strings.Builder
119 handlersRaw.WriteByte('[')
120 for _, hr := range r.HandlersRaw {
121 handlersRaw.WriteByte(' ')
122 handlersRaw.WriteString(string(hr))
123 }
124 handlersRaw.WriteByte(']')
125
126 return fmt.Sprintf(`{Group:"%s" MatcherSetsRaw:%s HandlersRaw:%s Terminal:%t}`,
127 r.Group, r.MatcherSetsRaw, handlersRaw.String(), r.Terminal)
128 }
129
130 // Provision sets up both the matchers and handlers in the route.
131 func (r *Route) Provision(ctx caddy.Context, metrics *Metrics) error {
132 err := r.ProvisionMatchers(ctx)
133 if err != nil {
134 return err
135 }
136 return r.ProvisionHandlers(ctx, metrics)
137 }
138
139 // ProvisionMatchers sets up all the matchers by loading the
140 // matcher modules. Only call this method directly if you need
141 // to set up matchers and handlers separately without having
142 // to provision a second time; otherwise use Provision instead.
143 func (r *Route) ProvisionMatchers(ctx caddy.Context) error {
144 // matchers
145 matchersIface, err := ctx.LoadModule(r, "MatcherSetsRaw")
146 if err != nil {
147 return fmt.Errorf("loading matcher modules: %v", err)
148 }
149 err = r.MatcherSets.FromInterface(matchersIface)
150 if err != nil {
151 return err
152 }
153 return nil
154 }
155
156 // ProvisionHandlers sets up all the handlers by loading the
157 // handler modules. Only call this method directly if you need
158 // to set up matchers and handlers separately without having
159 // to provision a second time; otherwise use Provision instead.
160 func (r *Route) ProvisionHandlers(ctx caddy.Context, metrics *Metrics) error {
161 handlersIface, err := ctx.LoadModule(r, "HandlersRaw")
162 if err != nil {
163 return fmt.Errorf("loading handler modules: %v", err)
164 }
165 for _, handler := range handlersIface.([]any) {
166 r.Handlers = append(r.Handlers, handler.(MiddlewareHandler))
167 }
168
169 // Store metrics info for route-level instrumentation (applied once
170 // per route in wrapRoute, instead of per-handler which was redundant).
171 r.metrics = metrics
172 r.metricsCtx = ctx
173 if len(r.Handlers) > 0 {
174 r.handlerName = caddy.GetModuleName(r.Handlers[0])
175 }
176
177 // Make ProvisionHandlers idempotent by clearing the middleware field
178 r.middleware = []Middleware{}
179
180 // pre-compile the middleware handler chain
181 for _, midhandler := range r.Handlers {
182 r.middleware = append(r.middleware, wrapMiddleware(ctx, midhandler))
183 }
184 return nil
185 }
186
187 // Compile prepares a middleware chain from the route list.
188 // This should only be done once during the request, just
189 // before the middleware chain is executed.
190 func (r Route) Compile(next Handler) Handler {
191 return wrapRoute(r)(next)
192 }
193
194 // RouteList is a list of server routes that can
195 // create a middleware chain.
196 type RouteList []Route
197
198 // Provision sets up both the matchers and handlers in the routes.
199 func (routes RouteList) Provision(ctx caddy.Context) error {
200 err := routes.ProvisionMatchers(ctx)
201 if err != nil {
202 return err
203 }
204 return routes.ProvisionHandlers(ctx, nil)
205 }
206
207 // ProvisionMatchers sets up all the matchers by loading the
208 // matcher modules. Only call this method directly if you need
209 // to set up matchers and handlers separately without having
210 // to provision a second time; otherwise use Provision instead.
211 func (routes RouteList) ProvisionMatchers(ctx caddy.Context) error {
212 for i := range routes {
213 err := routes[i].ProvisionMatchers(ctx)
214 if err != nil {
215 return fmt.Errorf("route %d: %v", i, err)
216 }
217 }
218 return nil
219 }
220
221 // ProvisionHandlers sets up all the handlers by loading the
222 // handler modules. Only call this method directly if you need
223 // to set up matchers and handlers separately without having
224 // to provision a second time; otherwise use Provision instead.
225 func (routes RouteList) ProvisionHandlers(ctx caddy.Context, metrics *Metrics) error {
226 for i := range routes {
227 err := routes[i].ProvisionHandlers(ctx, metrics)
228 if err != nil {
229 return fmt.Errorf("route %d: %v", i, err)
230 }
231 }
232 return nil
233 }
234
235 // Compile prepares a middleware chain from the route list.
236 // This should only be done either once during provisioning
237 // for top-level routes, or on each request just before the
238 // middleware chain is executed for subroutes.
239 func (routes RouteList) Compile(next Handler) Handler {
240 mid := make([]Middleware, 0, len(routes))
241 for _, route := range routes {
242 mid = append(mid, wrapRoute(route))
243 }
244 stack := next
245 for _, middleware := range slices.Backward(mid) {
246 stack = middleware(stack)
247 }
248 return stack
249 }
250
251 // wrapRoute wraps route with a middleware and handler so that it can
252 // be chained in and defer evaluation of its matchers to request-time.
253 // Like wrapMiddleware, it is vital that this wrapping takes place in
254 // its own stack frame so as to not overwrite the reference to the
255 // intended route by looping and changing the reference each time.
256 func wrapRoute(route Route) Middleware {
257 return func(next Handler) Handler {
258 return HandlerFunc(func(rw http.ResponseWriter, req *http.Request) error {
259 // TODO: Update this comment, it seems we've moved the copy into the handler?
260 // copy the next handler (it's an interface, so it's just
261 // a very lightweight copy of a pointer); this is important
262 // because this is a closure to the func below, which
263 // re-assigns the value as it compiles the middleware stack;
264 // if we don't make this copy, we'd affect the underlying
265 // pointer for all future request (yikes); we could
266 // alternatively solve this by moving the func below out of
267 // this closure and into a standalone package-level func,
268 // but I just thought this made more sense
269 nextCopy := next
270
271 // if route is part of a group, ensure only the
272 // first matching route in the group is applied
273 var groups map[string]struct{}
274 if route.Group != "" {
275 groups = req.Context().Value(routeGroupCtxKey).(map[string]struct{})
276
277 if _, ok := groups[route.Group]; ok {
278 // this group has already been satisfied by a
279 // matching route; skip without running matchers
280 return nextCopy.ServeHTTP(rw, req)
281 }
282 }
283
284 // route must match at least one of the matcher sets
285 matches, err := route.MatcherSets.AnyMatchWithError(req)
286 if err != nil {
287 // allow matchers the opportunity to short circuit
288 // the request and trigger the error handling chain
289 return err
290 }
291 if !matches {
292 // call the next handler, and skip this one,
293 // since the matcher didn't match
294 return nextCopy.ServeHTTP(rw, req)
295 }
296
297 // this matching route satisfies the group
298 if groups != nil {
299 groups[route.Group] = struct{}{}
300 }
301
302 // make terminal routes terminate
303 if route.Terminal {
304 if _, ok := req.Context().Value(ErrorCtxKey).(error); ok {
305 nextCopy = errorEmptyHandler
306 } else {
307 nextCopy = emptyHandler
308 }
309 }
310
311 // compile this route's handler stack
312 for _, middleware := range slices.Backward(route.middleware) {
313 nextCopy = middleware(nextCopy)
314 }
315
316 // Apply metrics instrumentation once for the entire route,
317 // rather than wrapping each individual handler. This avoids
318 // redundant metrics collection that caused significant CPU
319 // overhead (see issue #4644).
320 if route.metrics != nil {
321 nextCopy = newMetricsInstrumentedRoute(
322 route.metricsCtx, route.handlerName, nextCopy, route.metrics,
323 )
324 }
325
326 return nextCopy.ServeHTTP(rw, req)
327 })
328 }
329 }
330
331 // wrapMiddleware wraps mh such that it can be correctly
332 // appended to a list of middleware in preparation for
333 // compiling into a handler chain.
334 func wrapMiddleware(ctx caddy.Context, mh MiddlewareHandler) Middleware {
335 return func(next Handler) Handler {
336 return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
337 // EXPERIMENTAL: Trace each module that gets invoked
338 if server, ok := r.Context().Value(ServerCtxKey).(*Server); ok && server != nil {
339 server.logTrace(mh)
340 }
341 return mh.ServeHTTP(w, r, next)
342 })
343 }
344 }
345
346 // MatcherSet is a set of matchers which
347 // must all match in order for the request
348 // to be matched successfully.
349 type MatcherSet []any
350
351 // Match returns true if the request matches all
352 // matchers in mset or if there are no matchers.
353 func (mset MatcherSet) Match(r *http.Request) bool {
354 for _, m := range mset {
355 if me, ok := m.(RequestMatcherWithError); ok {
356 match, _ := me.MatchWithError(r)
357 if !match {
358 return false
359 }
360 continue
361 }
362 if me, ok := m.(RequestMatcher); ok {
363 if !me.Match(r) {
364 return false
365 }
366 continue
367 }
368 return false
369 }
370 return true
371 }
372
373 // MatchWithError returns true if r matches m.
374 func (mset MatcherSet) MatchWithError(r *http.Request) (bool, error) {
375 for _, m := range mset {
376 if me, ok := m.(RequestMatcherWithError); ok {
377 match, err := me.MatchWithError(r)
378 if err != nil || !match {
379 return match, err
380 }
381 continue
382 }
383 if me, ok := m.(RequestMatcher); ok {
384 if !me.Match(r) {
385 // for backwards compatibility
386 err, ok := GetVar(r.Context(), MatcherErrorVarKey).(error)
387 if ok {
388 // clear out the error from context since we've consumed it
389 SetVar(r.Context(), MatcherErrorVarKey, nil)
390 return false, err
391 }
392 return false, nil
393 }
394 continue
395 }
396 return false, fmt.Errorf("matcher is not a RequestMatcher or RequestMatcherWithError: %#v", m)
397 }
398 return true, nil
399 }
400
401 // RawMatcherSets is a group of matcher sets
402 // in their raw, JSON form.
403 type RawMatcherSets []caddy.ModuleMap
404
405 // MatcherSets is a group of matcher sets capable
406 // of checking whether a request matches any of
407 // the sets.
408 type MatcherSets []MatcherSet
409
410 // AnyMatch returns true if req matches any of the
411 // matcher sets in ms or if there are no matchers,
412 // in which case the request always matches.
413 //
414 // Deprecated: Use AnyMatchWithError instead.
415 func (ms MatcherSets) AnyMatch(req *http.Request) bool {
416 for _, m := range ms {
417 match, err := m.MatchWithError(req)
418 if err != nil {
419 SetVar(req.Context(), MatcherErrorVarKey, err)
420 return false
421 }
422 if match {
423 return match
424 }
425 }
426 return len(ms) == 0
427 }
428
429 // AnyMatchWithError returns true if req matches any of the
430 // matcher sets in ms or if there are no matchers, in which
431 // case the request always matches. If any matcher returns
432 // an error, we cut short and return the error.
433 func (ms MatcherSets) AnyMatchWithError(req *http.Request) (bool, error) {
434 for _, m := range ms {
435 match, err := m.MatchWithError(req)
436 if err != nil || match {
437 return match, err
438 }
439 }
440 return len(ms) == 0, nil
441 }
442
443 // FromInterface fills ms from an 'any' value obtained from LoadModule.
444 func (ms *MatcherSets) FromInterface(matcherSets any) error {
445 for _, matcherSetIfaces := range matcherSets.([]map[string]any) {
446 var matcherSet MatcherSet
447 for _, matcher := range matcherSetIfaces {
448 if m, ok := matcher.(RequestMatcherWithError); ok {
449 matcherSet = append(matcherSet, m)
450 continue
451 }
452 if m, ok := matcher.(RequestMatcher); ok {
453 matcherSet = append(matcherSet, m)
454 continue
455 }
456 return fmt.Errorf("decoded module is not a RequestMatcher or RequestMatcherWithError: %#v", matcher)
457 }
458 *ms = append(*ms, matcherSet)
459 }
460 return nil
461 }
462
463 // TODO: Is this used?
464 func (ms MatcherSets) String() string {
465 var result strings.Builder
466 result.WriteByte('[')
467 for _, matcherSet := range ms {
468 for _, matcher := range matcherSet {
469 fmt.Fprintf(&result, " %#v", matcher)
470 }
471 }
472 result.WriteByte(']')
473 return result.String()
474 }
475
476 var routeGroupCtxKey = caddy.CtxKey("route_group")