1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-23 11:56:00 +00:00

add my new trie data structure implementation written from scratch and specifically designed for HTTP (and Iris) - see https://github.com/kataras/muxie for the net/http version of it

Former-commit-id: 4eed1585f29b57418b61f6de058f5d6db4bb98bf
This commit is contained in:
Gerasimos (Makis) Maropoulos
2018-10-15 10:49:09 +03:00
parent 29a4354e1d
commit 3002736086
24 changed files with 575 additions and 648 deletions

View File

@@ -11,7 +11,6 @@ import (
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/errors"
"github.com/kataras/iris/core/netutil"
"github.com/kataras/iris/core/router/node"
)
// RequestHandler the middle man between acquiring a context and releasing it.
@@ -25,25 +24,17 @@ type RequestHandler interface {
RouteExists(ctx context.Context, method, path string) bool
}
type tree struct {
Method string
// subdomain is empty for default-hostname routes,
// ex: mysubdomain.
Subdomain string
Nodes *node.Nodes
}
type routerHandler struct {
trees []*tree
trees []*trie
hosts bool // true if at least one route contains a Subdomain.
}
var _ RequestHandler = &routerHandler{}
func (h *routerHandler) getTree(method, subdomain string) *tree {
func (h *routerHandler) getTree(method, subdomain string) *trie {
for i := range h.trees {
t := h.trees[i]
if t.Method == method && t.Subdomain == subdomain {
if t.method == method && t.subdomain == subdomain {
return t
}
}
@@ -63,12 +54,14 @@ func (h *routerHandler) addRoute(r *Route) error {
t := h.getTree(method, subdomain)
if t == nil {
n := node.Nodes{}
n := newTrieNode()
// first time we register a route to this method with this subdomain
t = &tree{Method: method, Subdomain: subdomain, Nodes: &n}
t = &trie{method: method, subdomain: subdomain, root: n}
h.trees = append(h.trees, t)
}
return t.Nodes.Add(routeName, path, handlers)
t.insert(path, routeName, handlers)
return nil
}
// NewDefaultHandler returns the handler which is responsible
@@ -188,11 +181,11 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
for i := range h.trees {
t := h.trees[i]
if method != t.Method {
if method != t.method {
continue
}
if h.hosts && t.Subdomain != "" {
if h.hosts && t.subdomain != "" {
requestHost := ctx.Host()
if netutil.IsLoopbackSubdomain(requestHost) {
// this fixes a bug when listening on
@@ -201,7 +194,7 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
continue // it's not a subdomain, it's something like 127.0.0.1 probably
}
// it's a dynamic wildcard subdomain, we have just to check if ctx.subdomain is not empty
if t.Subdomain == SubdomainWildcardIndicator {
if t.subdomain == SubdomainWildcardIndicator {
// mydomain.com -> invalid
// localhost -> invalid
// sub.mydomain.com -> valid
@@ -219,14 +212,14 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
continue
}
// continue to that, any subdomain is valid.
} else if !strings.HasPrefix(requestHost, t.Subdomain) { // t.Subdomain contains the dot.
} else if !strings.HasPrefix(requestHost, t.subdomain) { // t.subdomain contains the dot.
continue
}
}
routeName, handlers := t.Nodes.Find(path, ctx.Params())
if len(handlers) > 0 {
ctx.SetCurrentRouteName(routeName)
ctx.Do(handlers)
n := t.search(path, ctx.Params())
if n != nil {
ctx.SetCurrentRouteName(n.RouteName)
ctx.Do(n.Handlers)
// found
return
}
@@ -237,15 +230,12 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
if ctx.Application().ConfigurationReadOnly().GetFireMethodNotAllowed() {
for i := range h.trees {
t := h.trees[i]
// a bit slower than previous implementation but @kataras let me to apply this change
// because it's more reliable.
//
// if `Configuration#FireMethodNotAllowed` is kept as defaulted(false) then this function will not
// run, therefore performance kept as before.
if t.Nodes.Exists(path) {
if h.subdomainAndPathAndMethodExists(ctx, t, "", path) {
// RCF rfc2616 https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
// The response MUST include an Allow header containing a list of valid methods for the requested resource.
ctx.Header("Allow", t.Method)
ctx.Header("Allow", t.method)
ctx.StatusCode(http.StatusMethodNotAllowed)
return
}
@@ -255,55 +245,55 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
ctx.StatusCode(http.StatusNotFound)
}
func (h *routerHandler) subdomainAndPathAndMethodExists(ctx context.Context, t *trie, method, path string) bool {
if method != "" && method != t.method {
return false
}
if h.hosts && t.subdomain != "" {
requestHost := ctx.Host()
if netutil.IsLoopbackSubdomain(requestHost) {
// this fixes a bug when listening on
// 127.0.0.1:8080 for example
// and have a wildcard subdomain and a route registered to root domain.
return false // it's not a subdomain, it's something like 127.0.0.1 probably
}
// it's a dynamic wildcard subdomain, we have just to check if ctx.subdomain is not empty
if t.subdomain == SubdomainWildcardIndicator {
// mydomain.com -> invalid
// localhost -> invalid
// sub.mydomain.com -> valid
// sub.localhost -> valid
serverHost := ctx.Application().ConfigurationReadOnly().GetVHost()
if serverHost == requestHost {
return false // it's not a subdomain, it's a full domain (with .com...)
}
dotIdx := strings.IndexByte(requestHost, '.')
slashIdx := strings.IndexByte(requestHost, '/')
if dotIdx > 0 && (slashIdx == -1 || slashIdx > dotIdx) {
// if "." was found anywhere but not at the first path segment (host).
} else {
return false
}
// continue to that, any subdomain is valid.
} else if !strings.HasPrefix(requestHost, t.subdomain) { // t.subdomain contains the dot.
return false
}
}
n := t.search(path, ctx.Params())
return n != nil
}
// RouteExists reports whether a particular route exists
// It will search from the current subdomain of context's host, if not inside the root domain.
func (h *routerHandler) RouteExists(ctx context.Context, method, path string) bool {
for i := range h.trees {
t := h.trees[i]
if method != t.Method {
continue
}
if h.hosts && t.Subdomain != "" {
requestHost := ctx.Host()
if netutil.IsLoopbackSubdomain(requestHost) {
// this fixes a bug when listening on
// 127.0.0.1:8080 for example
// and have a wildcard subdomain and a route registered to root domain.
continue // it's not a subdomain, it's something like 127.0.0.1 probably
}
// it's a dynamic wildcard subdomain, we have just to check if ctx.subdomain is not empty
if t.Subdomain == SubdomainWildcardIndicator {
// mydomain.com -> invalid
// localhost -> invalid
// sub.mydomain.com -> valid
// sub.localhost -> valid
serverHost := ctx.Application().ConfigurationReadOnly().GetVHost()
if serverHost == requestHost {
continue // it's not a subdomain, it's a full domain (with .com...)
}
dotIdx := strings.IndexByte(requestHost, '.')
slashIdx := strings.IndexByte(requestHost, '/')
if dotIdx > 0 && (slashIdx == -1 || slashIdx > dotIdx) {
// if "." was found anywhere but not at the first path segment (host).
} else {
continue
}
// continue to that, any subdomain is valid.
} else if !strings.HasPrefix(requestHost, t.Subdomain) { // t.Subdomain contains the dot.
continue
}
}
_, handlers := t.Nodes.Find(path, ctx.Params())
if len(handlers) > 0 {
// found
if h.subdomainAndPathAndMethodExists(ctx, t, method, path) {
return true
}
// not found or method not allowed.
break
}
return false