1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-10 05:25:58 +00:00

Update to 8.0.2. Read HISTORY.md for the surpise

Former-commit-id: bbdf020ccaa986c332716aa7f749b7bdc24e427e
This commit is contained in:
kataras
2017-07-15 17:40:29 +03:00
parent 5015d92ece
commit 5752625c80
12 changed files with 508 additions and 31 deletions

View File

@@ -53,7 +53,7 @@ func (h *routerHandler) addRoute(method, subdomain, path string, handlers contex
t := h.getTree(method, subdomain)
if t == nil {
n := make(node.Nodes, 0)
n := node.Nodes{}
// first time we register a route to this method with this subdomain
t = &tree{Method: method, Subdomain: subdomain, Nodes: &n}
h.trees = append(h.trees, t)
@@ -81,7 +81,34 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
// sort, subdomains goes first.
sort.Slice(registeredRoutes, func(i, j int) bool {
return len(registeredRoutes[i].Subdomain) >= len(registeredRoutes[j].Subdomain)
first, second := registeredRoutes[i], registeredRoutes[j]
lsub1 := len(first.Subdomain)
lsub2 := len(second.Subdomain)
firstSlashLen := strings.Count(first.Path, "/")
secondSlashLen := strings.Count(second.Path, "/")
if lsub1 == lsub2 && first.Method == second.Method {
if secondSlashLen < firstSlashLen {
// fixes order when wildcard root is registered before other wildcard paths
return true
}
if secondSlashLen == firstSlashLen {
// fixes order when static path with the same prefix with a wildcard path
// is registered after the wildcard path, although this is managed
// by the low-level node but it couldn't work if we registered a root level wildcard, this fixes it.
if len(first.Tmpl().Params) == 0 {
return false
}
if len(second.Tmpl().Params) == 0 {
return true
}
}
}
// the rest are handled inside the node
return lsub1 > lsub2
})
rp := errors.NewReporter()

View File

@@ -15,9 +15,11 @@ type node struct {
s string
wildcardParamName string // name of the wildcard parameter, only one per whole Node is allowed
paramNames []string // only-names
children Nodes
childrenNodes Nodes
handlers context.Handlers
root bool
rootWildcard bool // if it's a wildcard {path} type on root, it should allow everything but it is not conflicts with
// any other static or dynamic or wildcard paths if exists on other nodes.
}
// ErrDublicate returnned from `Add` when two or more routes have the same registered path.
@@ -25,6 +27,7 @@ var ErrDublicate = errors.New("two or more routes have the same registered path"
// Add adds a node to the tree, returns an ErrDublicate error on failure.
func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
// println("[Add] adding path: " + path)
// resolve params and if that node should be added as root
var params []string
var paramStart, paramEnd int
@@ -59,11 +62,13 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
}
for _, idx := range p {
// print("-2 nodes.Add: path: " + path + " params len: ")
// println(len(params))
if err := nodes.add(path[:idx], nil, nil, true); err != nil {
return err
}
// print("-1 nodes.Add: path: " + path + " params len: ")
// println(len(params))
if nidx := idx + 1; len(path) > nidx {
if err := nodes.add(path[:nidx], nil, nil, true); err != nil {
return err
@@ -71,6 +76,8 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
}
}
// print("nodes.Add: path: " + path + " params len: ")
// println(len(params))
if err := nodes.add(path, params, handlers, true); err != nil {
return err
}
@@ -81,6 +88,9 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
}
func (nodes *Nodes) add(path string, paramNames []string, handlers context.Handlers, root bool) (err error) {
// println("[add] adding path: " + path)
// wraia etsi doulevei ara
// na to kanw na exei to node to diko tou wildcard parameter name
// kai sto telos na pernei auto, me vasi to *paramname
@@ -91,11 +101,31 @@ func (nodes *Nodes) add(path string, paramNames []string, handlers context.Handl
wildcardParamName := ""
if wildcardIdx > 0 {
wildcardParamName = path[wildcardIdx+1:]
path = path[0:wildcardIdx-1] + "/" // replace *paramName with single slash
// if root wildcard, then add it as it's and return
if path == "/" {
path += "/" // if root wildcard, then do it like "//" instead of simple "/"
n := &node{
rootWildcard: true,
s: path,
wildcardParamName: wildcardParamName,
paramNames: paramNames,
handlers: handlers,
root: root,
}
*nodes = append(*nodes, n)
// println("1. nodes.Add path: " + path)
return
}
}
loop:
for _, n := range *nodes {
if n.rootWildcard {
continue
}
minlen := len(n.s)
if len(path) < minlen {
@@ -112,12 +142,12 @@ loop:
*n = node{
s: n.s[:i],
children: Nodes{
childrenNodes: Nodes{
{
s: n.s[i:],
wildcardParamName: wildcardParamName,
wildcardParamName: n.wildcardParamName, // wildcardParamName
paramNames: n.paramNames,
children: n.children,
childrenNodes: n.childrenNodes,
handlers: n.handlers,
},
{
@@ -129,20 +159,25 @@ loop:
},
root: n.root,
}
// fmt.Printf("%#v\n", n)
// println("2. change n and return " + n.s[:i] + " and " + path[i:])
return
}
if len(path) < len(n.s) {
// println("3. change n and return | n.s[:len(path)] = " + n.s[:len(path)-1] + " and child: " + n.s[len(path)-1:])
*n = node{
s: n.s[:len(path)],
wildcardParamName: wildcardParamName,
paramNames: paramNames,
children: Nodes{
childrenNodes: Nodes{
{
s: n.s[len(path):],
wildcardParamName: wildcardParamName,
wildcardParamName: n.wildcardParamName, // wildcardParamName
paramNames: n.paramNames,
children: n.children,
childrenNodes: n.childrenNodes,
handlers: n.handlers,
},
},
@@ -154,13 +189,27 @@ loop:
}
if len(path) > len(n.s) {
err = n.children.add(path[len(n.s):], paramNames, handlers, false)
if n.wildcardParamName != "" {
n := &node{
s: path,
wildcardParamName: wildcardParamName,
paramNames: paramNames,
handlers: handlers,
root: root,
}
// println("3.5. nodes.Add path: " + n.s)
*nodes = append(*nodes, n)
return
}
// println("4. nodes.Add path: " + path[len(n.s):])
err = n.childrenNodes.add(path[len(n.s):], paramNames, handlers, false)
return err
}
if len(handlers) == 0 { // missing handlers
return nil
}
if len(n.handlers) > 0 { // n.handlers already setted
return ErrDublicate
}
@@ -177,7 +226,7 @@ loop:
handlers: handlers,
root: root,
}
// println("5. nodes.Add path: " + n.s)
*nodes = append(*nodes, n)
return
}
@@ -190,7 +239,14 @@ func (nodes Nodes) Find(path string, params *context.RequestParams) context.Hand
// map the params,
// n.params are the param names
if len(paramValues) > 0 {
// println("-----------")
// print("param values returned len: ")
// println(len(paramValues))
// println("first value is: " + paramValues[0])
// print("n.paramNames len: ")
// println(len(n.paramNames))
for i, name := range n.paramNames {
// println("setting param name: " + name + " = " + paramValues[i])
params.Set(name, paramValues[i])
}
// last is the wildcard,
@@ -198,7 +254,9 @@ func (nodes Nodes) Find(path string, params *context.RequestParams) context.Hand
// Note that n.wildcardParamName can be not empty but that doesn't meaning
// that it contains a wildcard path, so the check is required.
if len(paramValues) > len(n.paramNames) {
// println("len(paramValues) > len(n.paramNames)")
lastWildcardVal := paramValues[len(paramValues)-1]
// println("setting wildcard param name: " + n.wildcardParamName + " = " + lastWildcardVal)
params.Set(n.wildcardParamName, lastWildcardVal)
}
}
@@ -209,6 +267,7 @@ func (nodes Nodes) Find(path string, params *context.RequestParams) context.Hand
}
func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
// println("request path: " + path)
for _, n := range nodes {
if n.s == ":" {
paramEnd := strings.IndexByte(path, '/')
@@ -218,10 +277,26 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
}
return n, append(params, path)
}
return n.children.findChild(path[paramEnd:], append(params, path[:paramEnd]))
return n.childrenNodes.findChild(path[paramEnd:], append(params, path[:paramEnd]))
}
// println("n.s: " + n.s)
// print("n.childrenNodes len: ")
// println(len(n.childrenNodes))
// print("n.root: ")
// println(n.root)
// by runtime check of:,
// if n.s == "//" && n.root && n.wildcardParamName != "" {
// but this will slow down, so we have a static field on the node itself:
if n.rootWildcard {
// println("return from n.rootWildcard")
// single root wildcard
return n, append(params, path[1:])
}
if !strings.HasPrefix(path, n.s) {
// fmt.Printf("---here root: %v, n.s: "+n.s+" and path: "+path+" is dynamic: %v , wildcardParamName: %s, children len: %v \n", n.root, n.isDynamic(), n.wildcardParamName, len(n.childrenNodes))
continue
}
@@ -232,16 +307,30 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
return n, params
}
child, childParamNames := n.children.findChild(path[len(n.s):], params)
child, childParamNames := n.childrenNodes.findChild(path[len(n.s):], params)
// print("childParamNames len: ")
// println(len(childParamNames))
// if len(childParamNames) > 0 {
// println("childParamsNames[0] = " + childParamNames[0])
// }
if child == nil || len(child.handlers) == 0 {
if n.s[len(n.s)-1] == '/' && !(n.root && (n.s == "/" || len(n.children) > 0)) {
if n.s[len(n.s)-1] == '/' && !(n.root && (n.s == "/" || len(n.childrenNodes) > 0)) {
if len(n.handlers) == 0 {
return nil, nil
}
// println("if child == nil.... | n.s = " + n.s)
// print("n.paramNames len: ")
// println(n.paramNames)
// print("n.wildcardParamName is: ")
// println(n.wildcardParamName)
// print("return n, append(params, path[len(n.s) | params: ")
// println(path[len(n.s):])
return n, append(params, path[len(n.s):])
}
continue
}
@@ -252,7 +341,7 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
// childLen returns all the children's and their children's length.
func (n *node) childLen() (i int) {
for _, n := range n.children {
for _, n := range n.childrenNodes {
i++
i += n.childLen()
}
@@ -260,12 +349,11 @@ func (n *node) childLen() (i int) {
}
func (n *node) isDynamic() bool {
return n.s == ":"
return n.s == ":" || n.wildcardParamName != "" || n.rootWildcard
}
// prioritize sets the static paths first.
func (nodes Nodes) prioritize() {
sort.Slice(nodes, func(i, j int) bool {
if nodes[i].isDynamic() {
return false
@@ -273,10 +361,11 @@ func (nodes Nodes) prioritize() {
if nodes[j].isDynamic() {
return true
}
return nodes[i].childLen() > nodes[j].childLen()
})
for _, n := range nodes {
n.children.prioritize()
n.childrenNodes.prioritize()
}
}

View File

@@ -0,0 +1,179 @@
// black-box testing
//
// see _examples/routing/main_test.go for the most common router tests that you may want to see,
// this is a test for the new feature that I just coded: wildcard "/{p:path}" on root without conflicts
package router_test
import (
"net/http"
"testing"
"github.com/kataras/iris"
"github.com/kataras/iris/context"
"github.com/kataras/iris/httptest"
)
const (
same_as_request_path = "same"
from_status_code = "from"
staticPathPrefixBody = "from the static path: "
prefix_static_path_following_by_request_path = "prefix_same"
)
type testRouteRequest struct {
method string
subdomain string
path string
expectedStatusCode int
expectedBody string
}
type testRoute struct {
method string
path string
handler context.Handler
requests []testRouteRequest
}
var h = func(ctx context.Context) {
ctx.WriteString(ctx.Path())
}
var h2 = func(ctx context.Context) {
ctx.StatusCode(iris.StatusForbidden) // ! 200 but send the body as expected,
// we need that kind of behavior to determinate which handler is executed for routes that
// both having wildcard path but first one is registered on root level.
ctx.WriteString(ctx.Path())
}
func h3(ctx context.Context) {
ctx.Writef(staticPathPrefixBody + ctx.Path())
}
func TestRouterWildcardDifferentPrefixPath(t *testing.T) {
var tt = []testRoute{
{"GET", "/s/{p:path}", h, []testRouteRequest{
{"GET", "", "/s/that/is/wildcard", iris.StatusOK, same_as_request_path},
{"GET", "", "/s/ok", iris.StatusOK, same_as_request_path},
}},
{"GET", "/som/{p:path}", h, []testRouteRequest{
{"GET", "", "/som/that/is/wildcard", iris.StatusOK, same_as_request_path},
{"GET", "", "/som/ok", iris.StatusOK, same_as_request_path},
}},
{"GET", "/some/{p:path}", h, []testRouteRequest{
{"GET", "", "/some/that/is/wildcard", iris.StatusOK, same_as_request_path},
{"GET", "", "/some1/that/is/wildcard", iris.StatusNotFound, from_status_code},
}},
}
testTheRoutes(t, tt, false)
}
func TestRouterWildcardAndStatic(t *testing.T) {
var tt = []testRoute{
{"GET", "/some/{p:path}", h2, []testRouteRequest{
{"GET", "", "/some/that/is/wildcard", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/some/did", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/some1/that/is/wildcard", iris.StatusNotFound, from_status_code},
}},
{"GET", "/some/static", h, []testRouteRequest{
{"GET", "", "/some/static", iris.StatusOK, same_as_request_path},
}},
}
testTheRoutes(t, tt, true)
}
func TestRouterWildcardRootMany(t *testing.T) {
var tt = []testRoute{
// all routes will be handlded by "h" because we added wildcard to root,
// this feature is very important and can remove noumerous of previous hacks on our apps.
{"GET", "/{p:path}", h, []testRouteRequest{
{"GET", "", "/this/is/wildcard/on/root", iris.StatusOK, same_as_request_path},
}}, // mormally, order matters, root should be registered at last
// but we change the front level order algorithm to put last these automatically
// see handler.go
{"GET", "/some/{p:path}", h2, []testRouteRequest{
{"GET", "", "/some/that/is/wildcard", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/some/did", iris.StatusForbidden, same_as_request_path},
}},
{"GET", "/some/static", h, []testRouteRequest{
{"GET", "", "/some/static", iris.StatusOK, same_as_request_path},
}},
{"GET", "/some1", h, []testRouteRequest{
{"GET", "", "/some1", iris.StatusOK, same_as_request_path},
// this will show up because of the first wildcard, as we wanted to do.
{"GET", "", "/some1/that/is/wildcard", iris.StatusOK, same_as_request_path},
}},
}
testTheRoutes(t, tt, true)
}
func TestRouterWildcardRootManyAndRootStatic(t *testing.T) {
var tt = []testRoute{
// all routes will be handlded by "h" because we added wildcard to root,
// this feature is very important and can remove noumerous of previous hacks on our apps.
{"GET", "/{p:path}", h, []testRouteRequest{
{"GET", "", "/other2almost/some", iris.StatusOK, same_as_request_path},
// it's a request to /other , not other/something, therefore the root wildcard is the handler
{"GET", "", "/other", iris.StatusOK, same_as_request_path},
}},
{"GET", "/", h, []testRouteRequest{
{"GET", "", "/", iris.StatusOK, same_as_request_path},
}},
{"GET", "/other/{paramother:path}", h2, []testRouteRequest{
{"GET", "", "/other/wildcard", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/other/wildcard/here", iris.StatusForbidden, same_as_request_path},
}},
{"GET", "/other2/{paramothersecond:path}", h2, []testRouteRequest{
{"GET", "", "/other2/wildcard", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/other2/more/than/one/path/parts", iris.StatusForbidden, same_as_request_path},
}},
{"GET", "/other2/static", h3, []testRouteRequest{
{"GET", "", "/other2/static", iris.StatusOK, prefix_static_path_following_by_request_path},
{"GET", "", "/other2/staticed", iris.StatusForbidden, same_as_request_path},
}},
}
testTheRoutes(t, tt, true)
}
func testTheRoutes(t *testing.T, tests []testRoute, debug bool) {
// build the api
app := iris.New()
for _, tt := range tests {
app.Handle(tt.method, tt.path, tt.handler)
}
// setup the test suite
e := httptest.New(t, app, httptest.Debug(debug))
// run the tests
for _, tt := range tests {
for _, req := range tt.requests {
method := req.method
if method == "" {
method = tt.method
}
ex := e.Request(tt.method, req.path)
if req.subdomain != "" {
ex.WithURL("http://" + req.subdomain + ".localhost:8080")
}
expectedBody := req.expectedBody
if req.expectedBody == same_as_request_path {
expectedBody = req.path
}
if req.expectedBody == from_status_code {
expectedBody = http.StatusText(req.expectedStatusCode)
}
if req.expectedBody == prefix_static_path_following_by_request_path {
expectedBody = staticPathPrefixBody + req.path
}
ex.Expect().Status(req.expectedStatusCode).Body().Equal(expectedBody)
}
}
}