Skip to content

Commit

Permalink
feat: wip per route options
Browse files Browse the repository at this point in the history
  • Loading branch information
tigerwill90 committed Oct 5, 2024
1 parent 36e451a commit fb7f2e1
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 27 deletions.
15 changes: 9 additions & 6 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ type Context interface {
Tree() *Tree
// Fox returns the Router instance.
Fox() *Router
// Reset resets the Context to its initial state, attaching the provided ResponseWriter and http.Request.
Reset(w ResponseWriter, r *http.Request)
// Reset resets the Context to its initial state, attaching the provided Router, ResponseWriter and http.Request.
Reset(fox *Router, w ResponseWriter, r *http.Request)
}

// cTx holds request-related information and allows interaction with the ResponseWriter.
Expand All @@ -99,8 +99,7 @@ type cTx struct {
skipNds *skippedNodes

// tree at allocation (read-only, no reset)
tree *Tree
// router at allocation (read-only, no reset)
tree *Tree
fox *Router
cachedQuery url.Values
path string
Expand All @@ -109,25 +108,27 @@ type cTx struct {
}

// Reset resets the Context to its initial state, attaching the provided ResponseWriter and http.Request.
func (c *cTx) Reset(w ResponseWriter, r *http.Request) {
func (c *cTx) Reset(fox *Router, w ResponseWriter, r *http.Request) {
c.req = r
c.w = w
c.path = ""
c.tsr = false
c.cachedQuery = nil
*c.params = (*c.params)[:0]
c.fox = fox
}

// reset resets the Context to its initial state, attaching the provided http.ResponseWriter and http.Request.
// Caution: You should always pass the original http.ResponseWriter to this method, not the ResponseWriter itself, to
// avoid wrapping the ResponseWriter within itself. Use wisely!
func (c *cTx) reset(w http.ResponseWriter, r *http.Request) {
func (c *cTx) reset(fox *Router, w http.ResponseWriter, r *http.Request) {
c.rec.reset(w)
c.req = r
c.w = &c.rec
c.path = ""
c.cachedQuery = nil
*c.params = (*c.params)[:0]
c.fox = fox
}

func (c *cTx) resetNil() {
Expand All @@ -136,6 +137,7 @@ func (c *cTx) resetNil() {
c.path = ""
c.cachedQuery = nil
*c.params = (*c.params)[:0]
c.fox = nil
}

// Request returns the *http.Request.
Expand Down Expand Up @@ -313,6 +315,7 @@ func (c *cTx) CloneWith(w ResponseWriter, r *http.Request) ContextCloser {
cp.w = w
cp.path = c.path
cp.cachedQuery = nil
cp.fox = c.fox
if cap(*c.params) > cap(*cp.params) {
// Grow cp.params to a least cap(c.params)
*cp.params = slices.Grow(*cp.params, cap(*c.params))
Expand Down
16 changes: 9 additions & 7 deletions fox.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,9 @@ func (fox *Router) ClientIPStrategyEnabled() bool {
func (fox *Router) NewTree() *Tree {
tree := new(Tree)
tree.mws = fox.mws
tree.fox = fox
tree.ipStrategy = fox.ipStrategy
tree.ignoreTrailingSlash = fox.ignoreTrailingSlash
tree.redirectTrailingSlash = fox.redirectTrailingSlash

// Pre instantiate nodes for common http verb
nds := make([]*node, len(commonVerbs))
Expand Down Expand Up @@ -283,7 +285,7 @@ func (fox *Router) Remove(method, path string) error {
// This API is EXPERIMENTAL and is likely to change in future release.
func (fox *Router) Lookup(w ResponseWriter, r *http.Request) (handler HandlerFunc, cc ContextCloser, tsr bool) {
tree := fox.tree.Load()
return tree.Lookup(w, r)
return tree.Lookup(fox, w, r)
}

// SkipMethod is used as a return value from WalkFunc to indicate that
Expand Down Expand Up @@ -380,7 +382,7 @@ func (fox *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {

tree := fox.tree.Load()
c := tree.ctx.Get().(*cTx)
c.reset(w, r)
c.reset(fox, w, r)

nds := *tree.nodes.Load()
index := findRootNode(r.Method, nds)
Expand All @@ -402,15 +404,15 @@ func (fox *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

if r.Method != http.MethodConnect && r.URL.Path != "/" && tsr {
if fox.ignoreTrailingSlash {
if n.route.ignoreTrailingSlash {
c.path = n.route.path
c.tsr = tsr
n.route.handler(c)
c.Close()
return
}

if fox.redirectTrailingSlash && target == CleanPath(target) {
if n.route.redirectTrailingSlash && target == CleanPath(target) {
// Reset params as it may have recorded wildcard segment (the context may still be used in a middleware)
*c.params = (*c.params)[:0]
c.tsr = false
Expand Down Expand Up @@ -442,7 +444,7 @@ NoMethodFallback:
} else {
// Since different method and route may match (e.g. GET /foo/bar & POST /foo/{name}), we cannot set the path and params.
for i := 0; i < len(nds); i++ {
if n, tsr := tree.lookup(nds[i], target, c, true); n != nil && (!tsr || fox.ignoreTrailingSlash) {
if n, tsr := tree.lookup(nds[i], target, c, true); n != nil && (!tsr || n.route.ignoreTrailingSlash) {
if sb.Len() > 0 {
sb.WriteString(", ")
}
Expand All @@ -462,7 +464,7 @@ NoMethodFallback:
var sb strings.Builder
for i := 0; i < len(nds); i++ {
if nds[i].key != r.Method {
if n, tsr := tree.lookup(nds[i], target, c, true); n != nil && (!tsr || fox.ignoreTrailingSlash) {
if n, tsr := tree.lookup(nds[i], target, c, true); n != nil && (!tsr || n.route.ignoreTrailingSlash) {
if sb.Len() > 0 {
sb.WriteString(", ")
}
Expand Down
1 change: 1 addition & 0 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func newTextContextOnly(fox *Router, w http.ResponseWriter, r *http.Request) *cT
c.req = r
c.rec.reset(w)
c.w = &c.rec
c.fox = fox
return c
}

Expand Down
28 changes: 14 additions & 14 deletions tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ import (
// fox.Tree().Lock()
// defer fox.Tree().Unlock()
type Tree struct {
ctx sync.Pool
nodes atomic.Pointer[[]*node]
fox *Router // TODO tree should be agnostic to the router
mws []middleware
ctx sync.Pool
ipStrategy ClientIPStrategy
nodes atomic.Pointer[[]*node]
mws []middleware
sync.Mutex
maxParams atomic.Uint32
maxDepth atomic.Uint32
maxParams atomic.Uint32
maxDepth atomic.Uint32
redirectTrailingSlash bool
ignoreTrailingSlash bool
}

// Handle registers a new handler for the given method and path. This function return an error if the route
Expand Down Expand Up @@ -162,7 +164,7 @@ func (t *Tree) Methods(path string) []string {
c.resetNil()
for i := range nds {
n, tsr := t.lookup(nds[i], path, c, true)
if n != nil && (!tsr || t.fox.redirectTrailingSlash || t.fox.ignoreTrailingSlash) {
if n != nil && (!tsr || n.route.redirectTrailingSlash || n.route.ignoreTrailingSlash) {
if methods == nil {
methods = make([]string, 0)
}
Expand All @@ -183,7 +185,7 @@ func (t *Tree) Methods(path string) []string {
// use by multiple goroutine and while mutation on Tree are ongoing. If there is a direct match or a tsr is possible,
// Lookup always return a HandlerFunc and a ContextCloser.
// This API is EXPERIMENTAL and is likely to change in future release.
func (t *Tree) Lookup(w ResponseWriter, r *http.Request) (handler HandlerFunc, cc ContextCloser, tsr bool) {
func (t *Tree) Lookup(fox *Router, w ResponseWriter, r *http.Request) (handler HandlerFunc, cc ContextCloser, tsr bool) {
nds := *t.nodes.Load()
index := findRootNode(r.Method, nds)

Expand All @@ -192,7 +194,7 @@ func (t *Tree) Lookup(w ResponseWriter, r *http.Request) (handler HandlerFunc, c
}

c := t.ctx.Get().(*cTx)
c.Reset(w, r)
c.Reset(fox, w, r)

target := r.URL.Path
if len(r.URL.RawPath) > 0 {
Expand Down Expand Up @@ -858,8 +860,6 @@ func (t *Tree) allocateContext() *cTx {
// This is a read only value, no reset, it's always the
// owner of the pool.
tree: t,
// This is a read only value, no reset.
fox: t.fox,
}
}

Expand Down Expand Up @@ -928,12 +928,12 @@ func (t *Tree) updateMaxDepth(max uint32) {
// newRoute create a new route, apply path options and apply middleware on the handler.
func (t *Tree) newRoute(path string, handler HandlerFunc, opts ...PathOption) *Route {
rte := &Route{
ipStrategy: t.fox.ipStrategy,
ipStrategy: t.ipStrategy,
base: handler,
path: path,
mws: t.mws,
redirectTrailingSlash: t.fox.redirectTrailingSlash,
ignoreTrailingSlash: t.fox.ignoreTrailingSlash,
redirectTrailingSlash: t.redirectTrailingSlash,
ignoreTrailingSlash: t.ignoreTrailingSlash,
}

for _, opt := range opts {
Expand Down

0 comments on commit fb7f2e1

Please sign in to comment.