Skip to content

Commit

Permalink
handle level 1 router
Browse files Browse the repository at this point in the history
add more comments

update comment

add example

fix benchmark not found

add comment and update test method
gin_integration_test.go#L407
  • Loading branch information
qm012 committed Jun 29, 2021
1 parent e3ee01d commit b04bf36
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ coverage.out
count.out
test
profile.out
tmp.out
tmp.out
46 changes: 43 additions & 3 deletions gin_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,33 @@ import (
"github.com/stretchr/testify/assert"
)

func testRequest(t *testing.T, url string) {
// params[0]=url example:http://127.0.0.1:8080/index (cannot be empty)
// params[1]=response body (custom compare content)
func testRequest(t *testing.T, params ...string) {

if len(params) == 0 {
t.Fatal("url cannot be empty")
}

tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client := &http.Client{Transport: tr}

resp, err := client.Get(url)
resp, err := client.Get(params[0])
assert.NoError(t, err)
defer resp.Body.Close()

body, ioerr := ioutil.ReadAll(resp.Body)
assert.NoError(t, ioerr)
assert.Equal(t, "it worked", string(body), "resp body should match")

var expected = "it worked"
if len(params) > 1 {
expected = params[1]
}
assert.Equal(t, expected, string(body), "resp body should match")
assert.Equal(t, "200 OK", resp.Status, "should get a 200")
}

Expand Down Expand Up @@ -373,3 +385,31 @@ func testGetRequestHandler(t *testing.T, h http.Handler, url string) {
assert.Equal(t, "it worked", w.Body.String(), "resp body should match")
assert.Equal(t, 200, w.Code, "should get a 200")
}

func TestRunDynamicRouting(t *testing.T) {
router := New()
router.GET("/aa/*xx", func(c *Context) { c.String(http.StatusOK, "/aa/*xx") })
router.GET("/ab/*xx", func(c *Context) { c.String(http.StatusOK, "/ab/*xx") })
router.GET("/:cc", func(c *Context) { c.String(http.StatusOK, "/:cc") })
router.GET("/:cc/cc", func(c *Context) { c.String(http.StatusOK, "/:cc/cc") })
router.GET("/get/test/abc/", func(c *Context) { c.String(http.StatusOK, "/get/test/abc/") })
router.GET("/get/:param/abc/", func(c *Context) { c.String(http.StatusOK, "/get/:param/abc/") })

ts := httptest.NewServer(router)
defer ts.Close()

testRequest(t, ts.URL+"/aa/aa", "/aa/*xx")
testRequest(t, ts.URL+"/ab/ab", "/ab/*xx")
testRequest(t, ts.URL+"/all", "/:cc")
testRequest(t, ts.URL+"/all/cc", "/:cc/cc")
testRequest(t, ts.URL+"/a/cc", "/:cc/cc")
testRequest(t, ts.URL+"/a", "/:cc")
testRequest(t, ts.URL+"/get/test/abc/", "/get/test/abc/")
testRequest(t, ts.URL+"/get/te/abc/", "/get/:param/abc/")
testRequest(t, ts.URL+"/get/xx/abc/", "/get/:param/abc/")
testRequest(t, ts.URL+"/get/tt/abc/", "/get/:param/abc/")
testRequest(t, ts.URL+"/get/a/abc/", "/get/:param/abc/")
testRequest(t, ts.URL+"/get/t/abc/", "/get/:param/abc/")
testRequest(t, ts.URL+"/get/aa/abc/", "/get/:param/abc/")
testRequest(t, ts.URL+"/get/abas/abc/", "/get/:param/abc/")
}
46 changes: 44 additions & 2 deletions tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package gin

import (
"bytes"
"math"
"net/url"
"strings"
"unicode"
Expand Down Expand Up @@ -405,11 +406,34 @@ type nodeValue struct {
// made if a handle exists with an extra (without the) trailing slash for the
// given path.
func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) {
var skipped *skip
// path: /abc/123/def
// level 1 router:abc
// level 2 router:123
// level 3 router:def
var (
skipped *skip
latestNode = &(*n) // not found `level 1 router` use latestNode

// match '/' count
// default root node n.path is '/' matchNum++
// matchNum < 2: `level 1 router` not found,the current node needs to be equal to latestNode
// matchNum >= 2: `level (2 or 3 or 4 or ...) router`: Normal handling
matchNum int // each match will accumulate
)
// if path = '/', no need to look for router
if len(path) == 1 {
matchNum = math.MaxUint8 / 2
}

walk: // Outer loop for walking the tree
for {
prefix := n.path

// match '/', If this condition is matched, the next route is found
if strings.HasSuffix(n.path, "/") || strings.Contains(n.fullPath, ":") || n.path == "" {
matchNum++
}

if len(path) > len(prefix) {
if path[:len(prefix)] == prefix {
path = path[len(prefix):]
Expand Down Expand Up @@ -437,6 +461,10 @@ walk: // Outer loop for walking the tree
continue walk
}
}
// level 1 router not found,the current node needs to be equal to latestNode
if matchNum < 2 {
n = latestNode
}

// If there is no wildcard pattern, recommend a redirection
if !n.wildChild {
Expand Down Expand Up @@ -483,6 +511,16 @@ walk: // Outer loop for walking the tree
if len(n.children) > 0 {
path = path[end:]
n = n.children[0]
// next node,the latestNode needs to be equal to currentNode and handle next router
latestNode = n
// not found router in (level 1 router and handle next node),skipped cannot execute
// example:
// * /:cc/cc
// call /a/cc expectations:match/200 Actual:match/200
// call /a/dd expectations:unmatch/404 Actual: panic
// call /addr/dd/aa expectations:unmatch/404 Actual: panic
// skipped: It can only be executed if the secondary route is not found
skipped = nil
continue walk
}

Expand Down Expand Up @@ -533,8 +571,12 @@ walk: // Outer loop for walking the tree
}
}
}

// path = n.path
if path == prefix {
// level 1 router not found and latestNode.wildChild is ture
if matchNum < 2 && latestNode.wildChild {
n = latestNode.children[len(latestNode.children)-1]
}
// We should have reached the node containing the handle.
// Check if this node has a handle registered.
if value.handlers = n.handlers; value.handlers != nil {
Expand Down
26 changes: 24 additions & 2 deletions tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ func TestTreeWildcard(t *testing.T) {
"/info/:user/public",
"/info/:user/project/:project",
"/info/:user/project/golang",
"/aa/*xx",
"/ab/*xx",
"/:cc",
"/:cc/cc",
"/get/test/abc/",
"/get/:param/abc/",
}
for _, route := range routes {
tree.addRoute(route, fakeHandler(route))
Expand Down Expand Up @@ -186,6 +192,22 @@ func TestTreeWildcard(t *testing.T) {
{"/info/gordon/public", false, "/info/:user/public", Params{Param{Key: "user", Value: "gordon"}}},
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{Key: "user", Value: "gordon"}, Param{Key: "project", Value: "go"}}},
{"/info/gordon/project/golang", false, "/info/:user/project/golang", Params{Param{Key: "user", Value: "gordon"}}},
{"/aa/aa", false, "/aa/*xx", Params{Param{Key: "xx", Value: "/aa"}}},
{"/ab/ab", false, "/ab/*xx", Params{Param{Key: "xx", Value: "/ab"}}},
{"/a", false, "/:cc", Params{Param{Key: "cc", Value: "a"}}},
// * level 1 router match param will be Intercept first
// new PR handle (/all /all/cc /a/cc)
{"/all", false, "/:cc", Params{Param{Key: "cc", Value: "ll"}}},
{"/all/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "ll"}}},
{"/a/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: ""}}},
{"/get/test/abc/", false, "/get/test/abc/", nil},
{"/get/te/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "te"}}},
{"/get/xx/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "xx"}}},
{"/get/tt/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "tt"}}},
{"/get/a/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "a"}}},
{"/get/t/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "t"}}},
{"/get/aa/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "aa"}}},
{"/get/abas/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "abas"}}},
})

checkPriorities(t, tree)
Expand Down Expand Up @@ -565,8 +587,8 @@ func TestTreeFindCaseInsensitivePath(t *testing.T) {
"/u/öpfêl",
"/v/Äpfêl/",
"/v/Öpfêl",
"/w/♬", // 3 byte
"/w/♭/", // 3 byte, last byte differs
"/w/♬", // 3 byte
"/w/♭/", // 3 byte, last byte differs
"/w/𠜎", // 4 byte
"/w/𠜏/", // 4 byte
longPath,
Expand Down

0 comments on commit b04bf36

Please sign in to comment.