wrapRoute wraps route with a middleware and handler so that it can be chained in and defer evaluation of its matchers to request-time. Like wrapMiddleware, it is vital that this wrapping takes place in its own stack frame so as to not overwrite the reference to the intended route by looping and chan
(route Route)
| 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 | // route must match at least one of the matcher sets |
| 272 | matches, err := route.MatcherSets.AnyMatchWithError(req) |
| 273 | if err != nil { |
| 274 | // allow matchers the opportunity to short circuit |
| 275 | // the request and trigger the error handling chain |
| 276 | return err |
| 277 | } |
| 278 | if !matches { |
| 279 | // call the next handler, and skip this one, |
| 280 | // since the matcher didn't match |
| 281 | return nextCopy.ServeHTTP(rw, req) |
| 282 | } |
| 283 | |
| 284 | // if route is part of a group, ensure only the |
| 285 | // first matching route in the group is applied |
| 286 | if route.Group != "" { |
| 287 | groups := req.Context().Value(routeGroupCtxKey).(map[string]struct{}) |
| 288 | |
| 289 | if _, ok := groups[route.Group]; ok { |
| 290 | // this group has already been |
| 291 | // satisfied by a matching route |
| 292 | return nextCopy.ServeHTTP(rw, req) |
| 293 | } |
| 294 | |
| 295 | // this matching route satisfies the group |
| 296 | groups[route.Group] = struct{}{} |
| 297 | } |
| 298 | |
| 299 | // make terminal routes terminate |
| 300 | if route.Terminal { |
| 301 | if _, ok := req.Context().Value(ErrorCtxKey).(error); ok { |
| 302 | nextCopy = errorEmptyHandler |
| 303 | } else { |
| 304 | nextCopy = emptyHandler |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | // compile this route's handler stack |
| 309 | for _, middleware := range slices.Backward(route.middleware) { |
| 310 | nextCopy = middleware(nextCopy) |
| 311 | } |
| 312 | |
| 313 | // Apply metrics instrumentation once for the entire route, |
no test coverage detected