mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
146 lines
3.5 KiB
Go
146 lines
3.5 KiB
Go
package iris
|
|
|
|
import (
|
|
"bytes"
|
|
"sync"
|
|
|
|
"github.com/kataras/iris/utils"
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
type (
|
|
tree struct {
|
|
station *Iris
|
|
method string
|
|
rootBranch *Branch
|
|
domain string
|
|
hosts bool //if domain != "" we set it directly on .Plant
|
|
cors bool // if cross domain allow enabled
|
|
pool sync.Pool
|
|
next *tree
|
|
}
|
|
|
|
// Garden is the main area which routes are planted/placed
|
|
Garden struct {
|
|
first *tree
|
|
}
|
|
)
|
|
|
|
// garden
|
|
|
|
func (g *Garden) visitAll(f func(i int, tr *tree)) {
|
|
t := g.first
|
|
i := 0
|
|
for t != nil {
|
|
|
|
f(i, t)
|
|
t = t.next
|
|
}
|
|
}
|
|
|
|
// visitAllBreak like visitAll but if true to the function then it breaks
|
|
func (g *Garden) visitAllBreak(f func(i int, tr *tree) bool) {
|
|
t := g.first
|
|
i := 0
|
|
for t != nil {
|
|
|
|
if f(i, t) {
|
|
break
|
|
}
|
|
t = t.next
|
|
}
|
|
}
|
|
|
|
func (g *Garden) last() (t *tree) {
|
|
|
|
t = g.first
|
|
for t.next != nil {
|
|
t = t.next
|
|
}
|
|
return
|
|
}
|
|
|
|
// getRootByMethodAndDomain returns the correct branch which it's method&domain is equal to the given method&domain, from a garden's tree
|
|
// trees with no domain means that their domain==""
|
|
func (g *Garden) getRootByMethodAndDomain(method string, domain string) (b *Branch) {
|
|
g.visitAll(func(i int, t *tree) {
|
|
if t.domain == domain && t.method == method {
|
|
b = t.rootBranch
|
|
}
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// Plant plants/adds a route to the garden
|
|
func (g *Garden) Plant(station *Iris, _route IRoute) {
|
|
method := _route.GetMethod()
|
|
domain := _route.GetDomain()
|
|
path := _route.GetPath()
|
|
theRoot := g.getRootByMethodAndDomain(method, domain)
|
|
if theRoot == nil {
|
|
theRoot = new(Branch)
|
|
theNewTree := newTree(station, method, theRoot, domain, len(domain) > 0, _route.HasCors())
|
|
if g.first == nil {
|
|
g.first = theNewTree
|
|
} else {
|
|
g.last().next = theNewTree
|
|
}
|
|
|
|
}
|
|
theRoot.AddBranch(domain+path, _route.GetMiddleware())
|
|
}
|
|
|
|
// tree
|
|
|
|
func newTree(station *Iris, method string, theRoot *Branch, domain string, hosts bool, hasCors bool) *tree {
|
|
t := &tree{station: station, method: method, rootBranch: theRoot, domain: domain, hosts: hosts, cors: hasCors, pool: station.newContextPool()}
|
|
return t
|
|
}
|
|
|
|
// serve serves the route
|
|
func (_tree *tree) serve(reqCtx *fasthttp.RequestCtx, path string) bool {
|
|
ctx := _tree.pool.Get().(*Context)
|
|
ctx.Reset(reqCtx)
|
|
middleware, params, mustRedirect := _tree.rootBranch.GetBranch(path, ctx.Params) // pass the parameters here for 0 allocation
|
|
if middleware != nil {
|
|
ctx.Params = params
|
|
ctx.middleware = middleware
|
|
//ctx.Request.Header.SetUserAgentBytes(DefaultUserAgent)
|
|
ctx.Do()
|
|
_tree.pool.Put(ctx)
|
|
return true
|
|
} else if mustRedirect && !_tree.station.config.DisablePathCorrection && !bytes.Equal(reqCtx.Method(), MethodConnectBytes) {
|
|
|
|
reqPath := path
|
|
pathLen := len(reqPath)
|
|
|
|
if pathLen > 1 {
|
|
|
|
if reqPath[pathLen-1] == '/' {
|
|
reqPath = reqPath[:pathLen-1] //remove the last /
|
|
} else {
|
|
//it has path prefix, it doesn't ends with / and it hasn't be found, then just add the slash
|
|
reqPath = reqPath + "/"
|
|
}
|
|
|
|
ctx.Request.URI().SetPath(reqPath)
|
|
urlToRedirect := utils.BytesToString(ctx.Request.RequestURI())
|
|
|
|
ctx.Redirect(urlToRedirect, 301) // StatusMovedPermanently
|
|
// RFC2616 recommends that a short note "SHOULD" be included in the
|
|
// response because older user agents may not understand 301/307.
|
|
// Shouldn't send the response for POST or HEAD; that leaves GET.
|
|
if _tree.method == MethodGet {
|
|
note := "<a href=\"" + utils.HTMLEscape(urlToRedirect) + "\">Moved Permanently</a>.\n"
|
|
ctx.Write(note)
|
|
}
|
|
_tree.pool.Put(ctx)
|
|
return true
|
|
}
|
|
}
|
|
|
|
_tree.pool.Put(ctx)
|
|
return false
|
|
}
|