summaryrefslogtreecommitdiff
path: root/router.go
diff options
context:
space:
mode:
authortjpcc <tjp@ctrl-c.club>2023-02-14 20:10:57 -0700
committertjpcc <tjp@ctrl-c.club>2023-02-14 20:13:25 -0700
commitfcf545c27c70fb795a631a42738c486c07092e83 (patch)
treef533c819c8f4a382f796235b739f8fece0313588 /router.go
parent18d69173b4e23a2edd8c07c35f7a5b927587e6d7 (diff)
Router improvements.
- test coverage for Router, not just PathTree - Router.Mount() now flattens routes into the parent router - Router.Use() implemented to set middleware on a router itself
Diffstat (limited to 'router.go')
-rw-r--r--router.go60
1 files changed, 30 insertions, 30 deletions
diff --git a/router.go b/router.go
index 50cc41f..1d8e93d 100644
--- a/router.go
+++ b/router.go
@@ -2,8 +2,6 @@ package gus
import (
"context"
- "crypto/tls"
- "net/url"
"strings"
"tildegit.org/tjp/gus/internal"
@@ -27,11 +25,18 @@ import (
// The zero value is a usable Router which will fail to match any requst path.
type Router struct {
tree internal.PathTree[Handler]
+
+ middleware []Middleware
+ routeAdded bool
}
// Route adds a handler to the router under a path pattern.
-func (r Router) Route(pattern string, handler Handler) {
+func (r *Router) Route(pattern string, handler Handler) {
+ for i := len(r.middleware) - 1; i >= 0; i-- {
+ handler = r.middleware[i](handler)
+ }
r.tree.Add(pattern, handler)
+ r.routeAdded = true
}
// Handler matches against the request path and dipatches to a route handler.
@@ -59,6 +64,8 @@ func (r Router) Handler(ctx context.Context, request *Request) *Response {
}
// Match returns the matched handler and captured path parameters, or nils.
+//
+// The returned handlers will be wrapped with any middleware attached to the router.
func (r Router) Match(request *Request) (Handler, map[string]string) {
handler, params := r.tree.Match(request.Path)
if handler == nil {
@@ -72,19 +79,27 @@ func (r Router) Match(request *Request) (Handler, map[string]string) {
// The prefix pattern may include segment :wildcards, but no *remainder segment. The
// mounted sub-router should have patterns which only include the portion of the path
// after whatever was matched by the prefix pattern.
-func (r Router) Mount(prefix string, subrouter *Router) {
+func (r *Router) Mount(prefix string, subrouter *Router) {
prefix = strings.TrimSuffix(prefix, "/")
- r.Route(prefix+"/*"+subrouterPathKey, func(ctx context.Context, request *Request) *Response {
- r := cloneRequest(request)
- r.Path = "/" + RouteParams(ctx)[subrouterPathKey]
- return subrouter.Handler(ctx, r)
- })
-
- // TODO: better approach. the above works but it's a little hacky
- // - add a method to PathTree that returns all the registered patterns
- // and their associated handlers
- // - have Mount pull those out of the subrouter, prepend the prefix to
- // all its patterns, and re-add them to the parent router.
+
+ for _, subroute := range subrouter.tree.Routes() {
+ r.Route(prefix+"/"+subroute.Pattern, subroute.Value)
+ }
+}
+
+// Use attaches a middleware to the router.
+//
+// Any routes set on the router will have their handlers decorated by the attached
+// middlewares in reverse order (the first middleware attached will be the outer-most:
+// first to see requests and the last to see responses).
+//
+// Use will panic if Route or Mount have already been called on the router -
+// middlewares must be set before any routes.
+func (r *Router) Use(mw Middleware) {
+ if r.routeAdded {
+ panic("all middlewares must be added prior to adding routes")
+ }
+ r.middleware = append(r.middleware, mw)
}
// RouteParams gathers captured path parameters from the request context.
@@ -104,18 +119,3 @@ const subrouterPathKey = "subrouter_path"
type routeParamsKeyType struct{}
var routeParamsKey = routeParamsKeyType{}
-
-func cloneRequest(start *Request) *Request {
- end := &Request{}
- *end = *start
-
- end.URL = &url.URL{}
- *end.URL = *start.URL
-
- if start.TLSState != nil {
- end.TLSState = &tls.ConnectionState{}
- *end.TLSState = *start.TLSState
- }
-
- return end
-}