From 2b651fbae2ea2d787aed897dc3ef1631e4c6b643 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 11 Dec 2015 13:51:01 +0800 Subject: [PATCH 1/4] reuse map in tree.Match --- filter.go | 10 +++--- router.go | 17 +++------- tree.go | 92 ++++++++++++++++++++++++---------------------------- tree_test.go | 72 +++++++++++++++++++++++----------------- 4 files changed, 94 insertions(+), 97 deletions(-) diff --git a/filter.go b/filter.go index 7dbc7028..cefd74a1 100644 --- a/filter.go +++ b/filter.go @@ -32,13 +32,13 @@ type FilterRouter struct { // ValidRouter checks if the current request is matched by this filter. // If the request is matched, the values of the URL parameters defined // by the filter pattern are also returned. -func (f *FilterRouter) ValidRouter(url string) (bool, map[string]string) { - isok, params := f.tree.Match(url) +func (f *FilterRouter) ValidRouter(url string, ctx *context.Context) bool { + isok := f.tree.Match(url, ctx) if isok == nil { - return false, nil + return false } if isok, ok := isok.(bool); ok { - return isok, params + return isok } - return false, nil + return false } diff --git a/router.go b/router.go index 01dae8aa..effc2b41 100644 --- a/router.go +++ b/router.go @@ -583,13 +583,7 @@ func (p *ControllerRegister) execFilter(context *beecontext.Context, pos int, ur if filterR.returnOnOutput && context.ResponseWriter.Started { return true } - if ok, params := filterR.ValidRouter(urlPath); ok { - for k, v := range params { - if context.Input.Params == nil { - context.Input.Params = make(map[string]string) - } - context.Input.Params[k] = v - } + if ok := filterR.ValidRouter(urlPath, context); ok { filterR.filterFunc(context) } if filterR.returnOnOutput && context.ResponseWriter.Started { @@ -677,19 +671,16 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) httpMethod = "DELETE" } if t, ok := p.routers[httpMethod]; ok { - runObject, p := t.Match(urlPath) + runObject := t.Match(urlPath, context) if r, ok := runObject.(*controllerInfo); ok { routerInfo = r findrouter = true - if splat, ok := p[":splat"]; ok { + if splat, ok := context.Input.Params[":splat"]; ok { splatlist := strings.Split(splat, "/") for k, v := range splatlist { - p[strconv.Itoa(k)] = v + context.Input.Params[strconv.Itoa(k)] = v } } - if p != nil { - context.Input.Params = p - } } } diff --git a/tree.go b/tree.go index bbda2840..e23ce2fc 100644 --- a/tree.go +++ b/tree.go @@ -19,6 +19,7 @@ import ( "regexp" "strings" + "github.com/astaxie/beego/context" "github.com/astaxie/beego/utils" ) @@ -277,64 +278,60 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, } // Match router to runObject & params -func (t *Tree) Match(pattern string) (runObject interface{}, params map[string]string) { +func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{}) { if len(pattern) == 0 || pattern[0] != '/' { - return nil, nil + return nil } - return t.match(splitPath(pattern), nil) + return t.match(splitPath(pattern), nil, ctx) } -func (t *Tree) match(segments []string, wildcardValues []string) (runObject interface{}, params map[string]string) { +func (t *Tree) match(segments []string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { // Handle leaf nodes: if len(segments) == 0 { for _, l := range t.leaves { - if ok, pa := l.match(wildcardValues); ok { - return l.runObject, pa + if ok := l.match(wildcardValues, ctx); ok { + return l.runObject } } if t.wildcard != nil { for _, l := range t.wildcard.leaves { - if ok, pa := l.match(wildcardValues); ok { - return l.runObject, pa + if ok := l.match(wildcardValues, ctx); ok { + return l.runObject } } - } - return nil, nil + return nil } seg, segs := segments[0], segments[1:] subTree, ok := t.fixrouters[seg] if ok { - runObject, params = subTree.match(segs, wildcardValues) + runObject = subTree.match(segs, wildcardValues, ctx) } else if len(segs) == 0 { //.json .xml if subindex := strings.LastIndex(seg, "."); subindex != -1 { subTree, ok = t.fixrouters[seg[:subindex]] if ok { - runObject, params = subTree.match(segs, wildcardValues) + runObject = subTree.match(segs, wildcardValues, ctx) if runObject != nil { - if params == nil { - params = make(map[string]string) - } - params[":ext"] = seg[subindex+1:] - return runObject, params + ctx.Input.Params[":ext"] = seg[subindex+1:] + return runObject } } } } if runObject == nil && t.wildcard != nil { - runObject, params = t.wildcard.match(segs, append(wildcardValues, seg)) + runObject = t.wildcard.match(segs, append(wildcardValues, seg), ctx) } if runObject == nil { for _, l := range t.leaves { - if ok, pa := l.match(append(wildcardValues, segments...)); ok { - return l.runObject, pa + if ok := l.match(append(wildcardValues, segments...), ctx); ok { + return l.runObject } } } - return runObject, params + return runObject } type leafInfo struct { @@ -347,47 +344,43 @@ type leafInfo struct { runObject interface{} } -func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string]string) { +func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok bool) { if leaf.regexps == nil { // has error if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 { if utils.InSlice(":", leaf.wildcards) { - params = make(map[string]string) j := 0 for _, v := range leaf.wildcards { if v == ":" { continue } - params[v] = "" + ctx.Input.Params[v] = "" j++ } - return true, params + return true } - return false, nil + return false } else if len(wildcardValues) == 0 { // static path - return true, nil + return true } // match * if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" { - params = make(map[string]string) - params[":splat"] = path.Join(wildcardValues...) - return true, params + ctx.Input.Params[":splat"] = path.Join(wildcardValues...) + return true } // match *.* if len(leaf.wildcards) == 3 && leaf.wildcards[0] == "." { - params = make(map[string]string) lastone := wildcardValues[len(wildcardValues)-1] strs := strings.SplitN(lastone, ".", 2) if len(strs) == 2 { - params[":ext"] = strs[1] + ctx.Input.Params[":ext"] = strs[1] } else { - params[":ext"] = "" + ctx.Input.Params[":ext"] = "" } - params[":path"] = path.Join(wildcardValues[:len(wildcardValues)-1]...) + "/" + strs[0] - return true, params + ctx.Input.Params[":path"] = path.Join(wildcardValues[:len(wildcardValues)-1]...) + "/" + strs[0] + return true } // match :id - params = make(map[string]string) j := 0 for _, v := range leaf.wildcards { if v == ":" { @@ -397,38 +390,37 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string lastone := wildcardValues[len(wildcardValues)-1] strs := strings.SplitN(lastone, ".", 2) if len(strs) == 2 { - params[":ext"] = strs[1] + ctx.Input.Params[":ext"] = strs[1] } else { - params[":ext"] = "" + ctx.Input.Params[":ext"] = "" } if len(wildcardValues[j:]) == 1 { - params[":path"] = strs[0] + ctx.Input.Params[":path"] = strs[0] } else { - params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0] + ctx.Input.Params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0] } - return true, params + return true } if len(wildcardValues) <= j { - return false, nil + return false } - params[v] = wildcardValues[j] + ctx.Input.Params[v] = wildcardValues[j] j++ } - if len(params) != len(wildcardValues) { - return false, nil + if len(ctx.Input.Params) != len(wildcardValues) { + return false } - return true, params + return true } if !leaf.regexps.MatchString(path.Join(wildcardValues...)) { - return false, nil + return false } - params = make(map[string]string) matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...)) for i, match := range matches[1:] { - params[leaf.wildcards[i]] = match + ctx.Input.Params[leaf.wildcards[i]] = match } - return true, params + return true } // "/" -> [] diff --git a/tree_test.go b/tree_test.go index 7fe70f51..22a7b6f1 100644 --- a/tree_test.go +++ b/tree_test.go @@ -14,7 +14,11 @@ package beego -import "testing" +import ( + "testing" + + "github.com/astaxie/beego/context" +) type testinfo struct { url string @@ -27,11 +31,11 @@ var routers []testinfo func init() { routers = make([]testinfo, 0) routers = append(routers, testinfo{"/topic/?:auth:int", "/topic", nil}) - routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth":"123"}}) + routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth": "123"}}) routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}}) - routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1",":auth":"2"}}) + routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1", ":auth": "2"}}) routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}}) - routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1",":auth":"123"}}) + routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1", ":auth": "123"}}) routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}}) routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}}) routers = append(routers, testinfo{"/", "/", nil}) @@ -69,13 +73,14 @@ func TestTreeRouters(t *testing.T) { for _, r := range routers { tr := NewTree() tr.AddRouter(r.url, "astaxie") - obj, param := tr.Match(r.requesturl) + ctx := context.NewContext() + obj := tr.Match(r.requesturl, ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal(r.url + " can't get obj ") } if r.params != nil { for k, v := range r.params { - if vv, ok := param[k]; !ok { + if vv, ok := ctx.Input.Params[k]; !ok { t.Fatal(r.url + " " + r.requesturl + " get param empty:" + k) } else if vv != v { t.Fatal(r.url + " " + r.requesturl + " should be:" + v + " get param:" + vv) @@ -91,47 +96,51 @@ func TestAddTree(t *testing.T) { tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie") t1 := NewTree() t1.AddTree("/v1/zl", tr) - obj, param := t1.Match("/v1/zl/shop/123/account") + ctx := context.NewContext() + obj := t1.Match("/v1/zl/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/zl/shop/:id/account can't get obj ") } - if param == nil { + if len(ctx.Input.Params) == 0 { t.Fatal("get param error") } - if param[":id"] != "123" { + if ctx.Input.Params[":id"] != "123" { t.Fatal("get :id param error") } - obj, param = t1.Match("/v1/zl/shop/123/ttt_1_12.html") + ctx.Input.Reset(ctx) + obj = t1.Match("/v1/zl/shop/123/ttt_1_12.html", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ") } - if param == nil { + if len(ctx.Input.Params) == 0 { t.Fatal("get param error") } - if param[":sd"] != "123" || param[":id"] != "1" || param[":page"] != "12" { + if ctx.Input.Params[":sd"] != "123" || ctx.Input.Params[":id"] != "1" || ctx.Input.Params[":page"] != "12" { t.Fatal("get :sd :id :page param error") } t2 := NewTree() t2.AddTree("/v1/:shopid", tr) - obj, param = t2.Match("/v1/zl/shop/123/account") + ctx.Input.Reset(ctx) + obj = t2.Match("/v1/zl/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/:shopid/shop/:id/account can't get obj ") } - if param == nil { + if len(ctx.Input.Params) == 0 { t.Fatal("get param error") } - if param[":id"] != "123" || param[":shopid"] != "zl" { + if ctx.Input.Params[":id"] != "123" || ctx.Input.Params[":shopid"] != "zl" { t.Fatal("get :id :shopid param error") } - obj, param = t2.Match("/v1/zl/shop/123/ttt_1_12.html") + ctx.Input.Reset(ctx) + obj = t2.Match("/v1/zl/shop/123/ttt_1_12.html", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ") } - if param == nil { + if len(ctx.Input.Params) == 0 { t.Fatal("get :shopid param error") } - if param[":sd"] != "123" || param[":id"] != "1" || param[":page"] != "12" || param[":shopid"] != "zl" { + if ctx.Input.Params[":sd"] != "123" || ctx.Input.Params[":id"] != "1" || ctx.Input.Params[":page"] != "12" || ctx.Input.Params[":shopid"] != "zl" { t.Fatal("get :sd :id :page :shopid param error") } } @@ -142,14 +151,15 @@ func TestAddTree2(t *testing.T) { tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie") t3 := NewTree() t3.AddTree("/:version(v1|v2)/:prefix", tr) - obj, param := t3.Match("/v1/zl/shop/123/account") + ctx := context.NewContext() + obj := t3.Match("/v1/zl/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/:version(v1|v2)/:prefix/shop/:id/account can't get obj ") } - if param == nil { + if len(ctx.Input.Params) == 0 { t.Fatal("get param error") } - if param[":id"] != "123" || param[":prefix"] != "zl" || param[":version"] != "v1" { + if ctx.Input.Params[":id"] != "123" || ctx.Input.Params[":prefix"] != "zl" || ctx.Input.Params[":version"] != "v1" { t.Fatal("get :id :prefix :version param error") } } @@ -160,17 +170,19 @@ func TestAddTree3(t *testing.T) { tr.AddRouter("/shop/:sd/account", "astaxie") t3 := NewTree() t3.AddTree("/table/:num", tr) - obj, param := t3.Match("/table/123/shop/123/account") + ctx := context.NewContext() + obj := t3.Match("/table/123/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/table/:num/shop/:sd/account can't get obj ") } - if param == nil { + if len(ctx.Input.Params) == 0 { t.Fatal("get param error") } - if param[":num"] != "123" || param[":sd"] != "123" { + if ctx.Input.Params[":num"] != "123" || ctx.Input.Params[":sd"] != "123" { t.Fatal("get :num :sd param error") } - obj, param = t3.Match("/table/123/create") + ctx.Input.Reset(ctx) + obj = t3.Match("/table/123/create", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/table/:num/create can't get obj ") } @@ -182,17 +194,19 @@ func TestAddTree4(t *testing.T) { tr.AddRouter("/shop/:sd/:account", "astaxie") t4 := NewTree() t4.AddTree("/:info:int/:num/:id", tr) - obj, param := t4.Match("/12/123/456/shop/123/account") + ctx := context.NewContext() + obj := t4.Match("/12/123/456/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ") } - if param == nil { + if len(ctx.Input.Params) == 0 { t.Fatal("get param error") } - if param[":info"] != "12" || param[":num"] != "123" || param[":id"] != "456" || param[":sd"] != "123" || param[":account"] != "account" { + if ctx.Input.Params[":info"] != "12" || ctx.Input.Params[":num"] != "123" || ctx.Input.Params[":id"] != "456" || ctx.Input.Params[":sd"] != "123" || ctx.Input.Params[":account"] != "account" { t.Fatal("get :info :num :id :sd :account param error") } - obj, param = t4.Match("/12/123/456/create") + ctx.Input.Reset(ctx) + obj = t4.Match("/12/123/456/create", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/:info:int/:num/:id/create can't get obj ") } From 52c4c1fb980b2456051d696af7a8d05ae7cc7525 Mon Sep 17 00:00:00 2001 From: Simon Rawet Date: Wed, 16 Dec 2015 10:37:21 +0100 Subject: [PATCH 2/4] Added MaxMemory limit to CopyBody() Beego only uses the MaxMemory flag when using go's built in functions for parsing forms. However the CopyBody() function have no limit an will coppy anny amount of data into memory using ioutil.ReedAll() on the request body whitout anny size validation or limit. This fix wrapps input.Requst.Body in a LimitedReader using the same memory limit as ParseFormOrMulitForm() --- context/input.go | 6 ++++-- router.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/context/input.go b/context/input.go index 9649e14f..27de61e8 100644 --- a/context/input.go +++ b/context/input.go @@ -17,6 +17,7 @@ package context import ( "bytes" "errors" + "io" "io/ioutil" "net/url" "reflect" @@ -296,8 +297,9 @@ func (input *BeegoInput) Session(key interface{}) interface{} { } // CopyBody returns the raw request body data as bytes. -func (input *BeegoInput) CopyBody() []byte { - requestbody, _ := ioutil.ReadAll(input.Context.Request.Body) +func (input *BeegoInput) CopyBody(MaxMemory int64) []byte { + safe := &io.LimitedReader{R:input.Context.Request.Body, N:MaxMemory} + requestbody, _ := ioutil.ReadAll(safe) input.Context.Request.Body.Close() bf := bytes.NewBuffer(requestbody) input.Context.Request.Body = ioutil.NopCloser(bf) diff --git a/router.go b/router.go index 01dae8aa..2fa2fde0 100644 --- a/router.go +++ b/router.go @@ -659,7 +659,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) if r.Method != "GET" && r.Method != "HEAD" { if BConfig.CopyRequestBody && !context.Input.IsUpload() { - context.Input.CopyBody() + context.Input.CopyBody(BConfig.MaxMemory) } context.Input.ParseFormOrMulitForm(BConfig.MaxMemory) } From 29752e25758f8cf3841c1e9631f4f3b155aabf1d Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 16 Dec 2015 23:11:03 +0800 Subject: [PATCH 3/4] refactor router --- context/input.go | 46 ++++++-- controller.go | 2 +- controller_test.go | 15 ++- filter_test.go | 2 +- router.go | 8 +- router_test.go | 2 +- tree.go | 257 +++++++++++++++++++++++---------------------- tree_test.go | 39 +++---- 8 files changed, 206 insertions(+), 165 deletions(-) diff --git a/context/input.go b/context/input.go index 9649e14f..3f73787d 100644 --- a/context/input.go +++ b/context/input.go @@ -33,6 +33,7 @@ var ( acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`) acceptsXMLRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`) acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`) + maxParam = 50 ) // BeegoInput operates the http request header, data, cookie and body. @@ -40,16 +41,17 @@ var ( type BeegoInput struct { Context *Context CruSession session.Store - Params map[string]string - Data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. + pnames []string + pvalues []string + data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. RequestBody []byte } // NewInput return BeegoInput generated by Context. func NewInput() *BeegoInput { return &BeegoInput{ - Params: make(map[string]string), - Data: make(map[interface{}]interface{}), + pvalues: make([]string, maxParam), + data: make(map[interface{}]interface{}), } } @@ -57,8 +59,8 @@ func NewInput() *BeegoInput { func (input *BeegoInput) Reset(ctx *Context) { input.Context = ctx input.CruSession = nil - input.Params = make(map[string]string) - input.Data = make(map[interface{}]interface{}) + input.pnames = input.pnames[:0] + input.data = nil input.RequestBody = []byte{} } @@ -254,14 +256,27 @@ func (input *BeegoInput) UserAgent() string { return input.Header("User-Agent") } +// ParamsLen return the length of the params +func (input *BeegoInput) ParamsLen() int { + return len(input.pnames) +} + // Param returns router param by a given key. func (input *BeegoInput) Param(key string) string { - if v, ok := input.Params[key]; ok { - return v + for i, v := range input.pnames { + if v == key { + return input.pvalues[i] + } } return "" } +// SetParam will set the param with key and value +func (input *BeegoInput) SetParam(key, val string) { + input.pvalues[len(input.pnames)] = val + input.pnames = append(input.pnames, key) +} + // Query returns input data item string by a given string. func (input *BeegoInput) Query(key string) string { if val := input.Param(key); val != "" { @@ -305,9 +320,17 @@ func (input *BeegoInput) CopyBody() []byte { return requestbody } +// Data return the implicit data in the input +func (input *BeegoInput) Data() map[interface{}]interface{} { + if input.data == nil { + input.data = make(map[interface{}]interface{}) + } + return input.data +} + // GetData returns the stored data in this context. func (input *BeegoInput) GetData(key interface{}) interface{} { - if v, ok := input.Data[key]; ok { + if v, ok := input.data[key]; ok { return v } return nil @@ -316,7 +339,10 @@ func (input *BeegoInput) GetData(key interface{}) interface{} { // SetData stores data with given key in this context. // This data are only available in this context. func (input *BeegoInput) SetData(key, val interface{}) { - input.Data[key] = val + if input.data == nil { + input.data = make(map[interface{}]interface{}) + } + input.data[key] = val } // ParseFormOrMulitForm parseForm or parseMultiForm based on Content-type diff --git a/controller.go b/controller.go index 826ad54d..ebe99c5c 100644 --- a/controller.go +++ b/controller.go @@ -105,7 +105,7 @@ func (c *Controller) Init(ctx *context.Context, controllerName, actionName strin c.AppController = app c.EnableRender = true c.EnableXSRF = true - c.Data = ctx.Input.Data + c.Data = ctx.Input.Data() c.methodMapping = make(map[string]func()) } diff --git a/controller_test.go b/controller_test.go index ebf38b2c..51d3a5b7 100644 --- a/controller_test.go +++ b/controller_test.go @@ -21,7 +21,8 @@ import ( ) func TestGetInt(t *testing.T) { - i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + i := context.NewInput() + i.SetParam("age", "40") ctx := &context.Context{Input: i} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt("age") @@ -31,7 +32,8 @@ func TestGetInt(t *testing.T) { } func TestGetInt8(t *testing.T) { - i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + i := context.NewInput() + i.SetParam("age", "40") ctx := &context.Context{Input: i} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt8("age") @@ -42,7 +44,8 @@ func TestGetInt8(t *testing.T) { } func TestGetInt16(t *testing.T) { - i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + i := context.NewInput() + i.SetParam("age", "40") ctx := &context.Context{Input: i} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt16("age") @@ -52,7 +55,8 @@ func TestGetInt16(t *testing.T) { } func TestGetInt32(t *testing.T) { - i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + i := context.NewInput() + i.SetParam("age", "40") ctx := &context.Context{Input: i} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt32("age") @@ -62,7 +66,8 @@ func TestGetInt32(t *testing.T) { } func TestGetInt64(t *testing.T) { - i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + i := context.NewInput() + i.SetParam("age", "40") ctx := &context.Context{Input: i} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt64("age") diff --git a/filter_test.go b/filter_test.go index b1cd9c94..d9928d8d 100644 --- a/filter_test.go +++ b/filter_test.go @@ -29,7 +29,7 @@ func init() { } var FilterUser = func(ctx *context.Context) { - ctx.Output.Body([]byte("i am " + ctx.Input.Params[":last"] + ctx.Input.Params[":first"])) + ctx.Output.Body([]byte("i am " + ctx.Input.Param(":last") + ctx.Input.Param(":first"))) } func TestFilter(t *testing.T) { diff --git a/router.go b/router.go index effc2b41..53469b21 100644 --- a/router.go +++ b/router.go @@ -466,8 +466,8 @@ func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) stri } func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) { - for k, subtree := range t.fixrouters { - u := path.Join(url, k) + for _, subtree := range t.fixrouters { + u := path.Join(url, subtree.prefix) ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod) if ok { return ok, u @@ -675,10 +675,10 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) if r, ok := runObject.(*controllerInfo); ok { routerInfo = r findrouter = true - if splat, ok := context.Input.Params[":splat"]; ok { + if splat := context.Input.Param(":splat"); splat != "" { splatlist := strings.Split(splat, "/") for k, v := range splatlist { - context.Input.Params[strconv.Itoa(k)] = v + context.Input.SetParam(strconv.Itoa(k), v) } } } diff --git a/router_test.go b/router_test.go index 9598f99c..b0ae7a18 100644 --- a/router_test.go +++ b/router_test.go @@ -45,7 +45,7 @@ func (tc *TestController) List() { } func (tc *TestController) Params() { - tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Params["0"] + tc.Ctx.Input.Params["1"] + tc.Ctx.Input.Params["2"])) + tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param("0") + tc.Ctx.Input.Param("1") + tc.Ctx.Input.Param("2"))) } func (tc *TestController) Myext() { diff --git a/tree.go b/tree.go index e23ce2fc..dbcc63c9 100644 --- a/tree.go +++ b/tree.go @@ -23,26 +23,28 @@ import ( "github.com/astaxie/beego/utils" ) +var ( + allowSuffixExt = []string{".json", ".xml", ".html"} +) + // Tree has three elements: FixRouter/wildcard/leaves // fixRouter sotres Fixed Router // wildcard stores params // leaves store the endpoint information type Tree struct { + //prefix set for static router + prefix string //search fix route first - fixrouters map[string]*Tree - + fixrouters []*Tree //if set, failure to match fixrouters search then search wildcard wildcard *Tree - //if set, failure to match wildcard search leaves []*leafInfo } // NewTree return a new Tree func NewTree() *Tree { - return &Tree{ - fixrouters: make(map[string]*Tree), - } + return &Tree{} } // AddTree will add tree to the exist Tree @@ -57,15 +59,31 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st } seg := segments[0] iswild, params, regexpStr := splitSegment(seg) + // if it's ? meaning can igone this, so add one more rule for it + if len(params) > 0 && params[0] == ":" { + params = params[1:] + if len(segments[1:]) > 0 { + t.addtree(segments[1:], tree, append(wildcards, params...), reg) + } else { + filterTreeWithPrefix(tree, wildcards, reg) + } + } + //Rule: /login/*/access match /login/2009/11/access + //if already has *, and when loop the access, should as a regexpStr + if !iswild && utils.InSlice(":splat", wildcards) { + iswild = true + regexpStr = seg + } + //Rule: /user/:id/* + if seg == "*" && len(wildcards) > 0 && reg == "" { + regexpStr = "(.+)" + } if len(segments) == 1 { if iswild { if regexpStr != "" { if reg == "" { rr := "" for _, w := range wildcards { - if w == "." || w == ":" { - continue - } if w == ":splat" { rr = rr + "(.+)/" } else { @@ -94,10 +112,12 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st } else { reg = strings.Trim(reg+"/"+regexpStr, "/") filterTreeWithPrefix(tree, append(wildcards, params...), reg) - t.fixrouters[seg] = tree + tree.prefix = seg + t.fixrouters = append(t.fixrouters, tree) } return } + if iswild { if t.wildcard == nil { t.wildcard = NewTree() @@ -106,9 +126,6 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st if reg == "" { rr := "" for _, w := range wildcards { - if w == "." || w == ":" { - continue - } if w == ":splat" { rr = rr + "(.+)/" } else { @@ -122,20 +139,23 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st } else if reg != "" { if seg == "*.*" { regexpStr = "([^.]+).(.+)" + params = params[1:] } else { - for _, w := range params { - if w == "." || w == ":" { - continue - } + for _ = range params { regexpStr = "([^/]+)/" + regexpStr } } + } else { + if seg == "*.*" { + params = params[1:] + } } reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/") t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg) } else { subTree := NewTree() - t.fixrouters[seg] = subTree + subTree.prefix = seg + t.fixrouters = append(t.fixrouters, subTree) subTree.addtree(segments[1:], tree, append(wildcards, params...), reg) } } @@ -154,9 +174,6 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) { l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$") } else { for _, v := range l.wildcards { - if v == ":" || v == "." { - continue - } if v == ":splat" { reg = reg + "/(.+)" } else { @@ -166,21 +183,10 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) { l.regexps = regexp.MustCompile("^" + reg + "$") l.wildcards = append(wildcards, l.wildcards...) } - filterCards := []string{} - for _, v := range l.wildcards { - if v == ":" || v == "." { - continue - } - filterCards = append(filterCards, v) - } - l.wildcards = filterCards } else { l.wildcards = append(wildcards, l.wildcards...) if l.regexps != nil { for _, w := range wildcards { - if w == "." || w == ":" { - continue - } if w == ":splat" { reg = "(.+)/" + reg } else { @@ -203,32 +209,26 @@ func (t *Tree) AddRouter(pattern string, runObject interface{}) { func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) { if len(segments) == 0 { if reg != "" { - filterCards := []string{} - for _, v := range wildcards { - if v == ":" || v == "." { - continue - } - filterCards = append(filterCards, v) - } - t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: filterCards, regexps: regexp.MustCompile("^" + reg + "$")}) + t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")}) } else { t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards}) } - for i, v := range wildcards { - if v == ":" { - t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards[:i+1]}) - } - } } else { seg := segments[0] iswild, params, regexpStr := splitSegment(seg) - //for the router /login/*/access match /login/2009/11/access + // if it's ? meaning can igone this, so add one more rule for it + if len(params) > 0 && params[0] == ":" { + t.addseg(segments[1:], route, wildcards, reg) + params = params[1:] + } + //Rule: /login/*/access match /login/2009/11/access + //if already has *, and when loop the access, should as a regexpStr if !iswild && utils.InSlice(":splat", wildcards) { iswild = true regexpStr = seg } + //Rule: /user/:id/* if seg == "*" && len(wildcards) > 0 && reg == "" { - iswild = true regexpStr = "(.+)" } if iswild { @@ -239,9 +239,6 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, if reg == "" { rr := "" for _, w := range wildcards { - if w == "." || w == ":" { - continue - } if w == ":splat" { rr = rr + "(.+)/" } else { @@ -255,22 +252,31 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, } else if reg != "" { if seg == "*.*" { regexpStr = "/([^.]+).(.+)" + params = params[1:] } else { - for _, w := range params { - - if w == "." || w == ":" { - continue - } + for _ = range params { regexpStr = "/([^/]+)" + regexpStr } } + } else { + if seg == "*.*" { + params = params[1:] + } } t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr) } else { - subTree, ok := t.fixrouters[seg] + var ok bool + var subTree *Tree + for _, subTree = range t.fixrouters { + if t.prefix == seg { + ok = true + break + } + } if !ok { subTree = NewTree() - t.fixrouters[seg] = subTree + subTree.prefix = seg + t.fixrouters = append(t.fixrouters, subTree) } subTree.addseg(segments[1:], route, wildcards, reg) } @@ -282,13 +288,18 @@ func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{ if len(pattern) == 0 || pattern[0] != '/' { return nil } - - return t.match(splitPath(pattern), nil, ctx) + return t.match(pattern, nil, ctx) } -func (t *Tree) match(segments []string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { +func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { + if len(pattern) > 0 { + i := 0 + for ; i < len(pattern) && pattern[i] == '/'; i++ { + } + pattern = pattern[i:] + } // Handle leaf nodes: - if len(segments) == 0 { + if len(pattern) == 0 { for _, l := range t.leaves { if ok := l.match(wildcardValues, ctx); ok { return l.runObject @@ -303,28 +314,47 @@ func (t *Tree) match(segments []string, wildcardValues []string, ctx *context.Co } return nil } - - seg, segs := segments[0], segments[1:] - - subTree, ok := t.fixrouters[seg] - if ok { - runObject = subTree.match(segs, wildcardValues, ctx) - } else if len(segs) == 0 { //.json .xml - if subindex := strings.LastIndex(seg, "."); subindex != -1 { - subTree, ok = t.fixrouters[seg[:subindex]] - if ok { - runObject = subTree.match(segs, wildcardValues, ctx) - if runObject != nil { - ctx.Input.Params[":ext"] = seg[subindex+1:] - return runObject + var seg string + i, l := 0, len(pattern) + for ; i < l && pattern[i] != '/'; i++ { + } + if i == 0 { + seg = pattern + pattern = "" + } else { + seg = pattern[:i] + pattern = pattern[i:] + } + for _, subTree := range t.fixrouters { + if subTree.prefix == seg { + runObject = subTree.match(pattern, wildcardValues, ctx) + if runObject != nil { + break + } + } + } + if runObject == nil { + // Filter the .json .xml .html extension + for _, str := range allowSuffixExt { + if strings.HasSuffix(seg, str) { + for _, subTree := range t.fixrouters { + if subTree.prefix == seg[:len(seg)-len(str)] { + runObject = subTree.match(pattern, wildcardValues, ctx) + if runObject != nil { + ctx.Input.SetParam(":ext", str[1:]) + } + } } } } } if runObject == nil && t.wildcard != nil { - runObject = t.wildcard.match(segs, append(wildcardValues, seg), ctx) + runObject = t.wildcard.match(pattern, append(wildcardValues, seg), ctx) } + if runObject == nil { + segments := splitPath(pattern) + wildcardValues = append(wildcardValues, seg) for _, l := range t.leaves { if ok := l.match(append(wildcardValues, segments...), ctx); ok { return l.runObject @@ -345,71 +375,48 @@ type leafInfo struct { } func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok bool) { + //fmt.Println("Leaf:", wildcardValues, leaf.wildcards, leaf.regexps) if leaf.regexps == nil { - // has error - if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 { - if utils.InSlice(":", leaf.wildcards) { - j := 0 - for _, v := range leaf.wildcards { - if v == ":" { - continue - } - ctx.Input.Params[v] = "" - j++ - } - return true - } - return false - } else if len(wildcardValues) == 0 { // static path + if len(wildcardValues) == 0 { // static path return true } // match * if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" { - ctx.Input.Params[":splat"] = path.Join(wildcardValues...) + ctx.Input.SetParam(":splat", path.Join(wildcardValues...)) return true } - // match *.* - if len(leaf.wildcards) == 3 && leaf.wildcards[0] == "." { - lastone := wildcardValues[len(wildcardValues)-1] - strs := strings.SplitN(lastone, ".", 2) - if len(strs) == 2 { - ctx.Input.Params[":ext"] = strs[1] - } else { - ctx.Input.Params[":ext"] = "" - } - ctx.Input.Params[":path"] = path.Join(wildcardValues[:len(wildcardValues)-1]...) + "/" + strs[0] - return true - } - // match :id - j := 0 - for _, v := range leaf.wildcards { - if v == ":" { - continue - } - if v == "." { + // match *.* or :id + if len(leaf.wildcards) >= 2 && leaf.wildcards[len(leaf.wildcards)-2] == ":path" && leaf.wildcards[len(leaf.wildcards)-1] == ":ext" { + if len(leaf.wildcards) == 2 { lastone := wildcardValues[len(wildcardValues)-1] strs := strings.SplitN(lastone, ".", 2) if len(strs) == 2 { - ctx.Input.Params[":ext"] = strs[1] - } else { - ctx.Input.Params[":ext"] = "" - } - if len(wildcardValues[j:]) == 1 { - ctx.Input.Params[":path"] = strs[0] - } else { - ctx.Input.Params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0] + ctx.Input.SetParam(":ext", strs[1]) } + ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[:len(wildcardValues)-1]...), strs[0])) return true - } - if len(wildcardValues) <= j { + } else if len(wildcardValues) < 2 { return false } - ctx.Input.Params[v] = wildcardValues[j] - j++ + var index int + for index = 0; index < len(leaf.wildcards)-2; index++ { + ctx.Input.SetParam(leaf.wildcards[index], wildcardValues[index]) + } + lastone := wildcardValues[len(wildcardValues)-1] + strs := strings.SplitN(lastone, ".", 2) + if len(strs) == 2 { + ctx.Input.SetParam(":ext", strs[1]) + } + ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[index:len(wildcardValues)-1]...), strs[0])) + return true } - if len(ctx.Input.Params) != len(wildcardValues) { + // match :id + if len(leaf.wildcards) != len(wildcardValues) { return false } + for j, v := range leaf.wildcards { + ctx.Input.SetParam(v, wildcardValues[j]) + } return true } @@ -418,7 +425,7 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b } matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...)) for i, match := range matches[1:] { - ctx.Input.Params[leaf.wildcards[i]] = match + ctx.Input.SetParam(leaf.wildcards[i], match) } return true } diff --git a/tree_test.go b/tree_test.go index 22a7b6f1..f0923c88 100644 --- a/tree_test.go +++ b/tree_test.go @@ -45,6 +45,7 @@ func init() { routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}}) routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}}) routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}}) + routers = append(routers, testinfo{"/cc/:id/*", "/cc/2009/11/dd", map[string]string{":id": "2009", ":splat": "11/dd"}}) routers = append(routers, testinfo{"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}}) routers = append(routers, testinfo{"/thumbnail/:size/uploads/*", "/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg", @@ -76,14 +77,14 @@ func TestTreeRouters(t *testing.T) { ctx := context.NewContext() obj := tr.Match(r.requesturl, ctx) if obj == nil || obj.(string) != "astaxie" { - t.Fatal(r.url + " can't get obj ") + t.Fatal(r.url+" can't get obj, Expect ", r.requesturl) } if r.params != nil { for k, v := range r.params { - if vv, ok := ctx.Input.Params[k]; !ok { + if vv := ctx.Input.Param(k); vv != v { + t.Fatal("The Rule: " + r.url + "\nThe RequestURL:" + r.requesturl + "\nThe Key is " + k + ", The Value should be: " + v + ", but get: " + vv) + } else if vv == "" && v != "" { t.Fatal(r.url + " " + r.requesturl + " get param empty:" + k) - } else if vv != v { - t.Fatal(r.url + " " + r.requesturl + " should be:" + v + " get param:" + vv) } } } @@ -101,10 +102,10 @@ func TestAddTree(t *testing.T) { if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/zl/shop/:id/account can't get obj ") } - if len(ctx.Input.Params) == 0 { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if ctx.Input.Params[":id"] != "123" { + if ctx.Input.Param(":id") != "123" { t.Fatal("get :id param error") } ctx.Input.Reset(ctx) @@ -112,10 +113,10 @@ func TestAddTree(t *testing.T) { if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ") } - if len(ctx.Input.Params) == 0 { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if ctx.Input.Params[":sd"] != "123" || ctx.Input.Params[":id"] != "1" || ctx.Input.Params[":page"] != "12" { + if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" { t.Fatal("get :sd :id :page param error") } @@ -126,10 +127,10 @@ func TestAddTree(t *testing.T) { if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/:shopid/shop/:id/account can't get obj ") } - if len(ctx.Input.Params) == 0 { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if ctx.Input.Params[":id"] != "123" || ctx.Input.Params[":shopid"] != "zl" { + if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":shopid") != "zl" { t.Fatal("get :id :shopid param error") } ctx.Input.Reset(ctx) @@ -137,10 +138,10 @@ func TestAddTree(t *testing.T) { if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ") } - if len(ctx.Input.Params) == 0 { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get :shopid param error") } - if ctx.Input.Params[":sd"] != "123" || ctx.Input.Params[":id"] != "1" || ctx.Input.Params[":page"] != "12" || ctx.Input.Params[":shopid"] != "zl" { + if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" || ctx.Input.Param(":shopid") != "zl" { t.Fatal("get :sd :id :page :shopid param error") } } @@ -156,10 +157,10 @@ func TestAddTree2(t *testing.T) { if obj == nil || obj.(string) != "astaxie" { t.Fatal("/:version(v1|v2)/:prefix/shop/:id/account can't get obj ") } - if len(ctx.Input.Params) == 0 { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if ctx.Input.Params[":id"] != "123" || ctx.Input.Params[":prefix"] != "zl" || ctx.Input.Params[":version"] != "v1" { + if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":prefix") != "zl" || ctx.Input.Param(":version") != "v1" { t.Fatal("get :id :prefix :version param error") } } @@ -175,10 +176,10 @@ func TestAddTree3(t *testing.T) { if obj == nil || obj.(string) != "astaxie" { t.Fatal("/table/:num/shop/:sd/account can't get obj ") } - if len(ctx.Input.Params) == 0 { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if ctx.Input.Params[":num"] != "123" || ctx.Input.Params[":sd"] != "123" { + if ctx.Input.Param(":num") != "123" || ctx.Input.Param(":sd") != "123" { t.Fatal("get :num :sd param error") } ctx.Input.Reset(ctx) @@ -199,10 +200,12 @@ func TestAddTree4(t *testing.T) { if obj == nil || obj.(string) != "astaxie" { t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ") } - if len(ctx.Input.Params) == 0 { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if ctx.Input.Params[":info"] != "12" || ctx.Input.Params[":num"] != "123" || ctx.Input.Params[":id"] != "456" || ctx.Input.Params[":sd"] != "123" || ctx.Input.Params[":account"] != "account" { + if ctx.Input.Param(":info") != "12" || ctx.Input.Param(":num") != "123" || + ctx.Input.Param(":id") != "456" || ctx.Input.Param(":sd") != "123" || + ctx.Input.Param(":account") != "account" { t.Fatal("get :info :num :id :sd :account param error") } ctx.Input.Reset(ctx) From dbc4ac6945492da8aa9e84cab015d59cdf6b8fff Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 16 Dec 2015 23:43:32 +0800 Subject: [PATCH 4/4] reduce the slicegrow --- context/input.go | 8 +++++--- tree.go | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/context/input.go b/context/input.go index 3f73787d..7e9f50b6 100644 --- a/context/input.go +++ b/context/input.go @@ -50,7 +50,8 @@ type BeegoInput struct { // NewInput return BeegoInput generated by Context. func NewInput() *BeegoInput { return &BeegoInput{ - pvalues: make([]string, maxParam), + pnames: make([]string, 0, maxParam), + pvalues: make([]string, 0, maxParam), data: make(map[interface{}]interface{}), } } @@ -60,6 +61,7 @@ func (input *BeegoInput) Reset(ctx *Context) { input.Context = ctx input.CruSession = nil input.pnames = input.pnames[:0] + input.pvalues = input.pvalues[:0] input.data = nil input.RequestBody = []byte{} } @@ -264,7 +266,7 @@ func (input *BeegoInput) ParamsLen() int { // Param returns router param by a given key. func (input *BeegoInput) Param(key string) string { for i, v := range input.pnames { - if v == key { + if v == key && i <= len(input.pvalues) { return input.pvalues[i] } } @@ -273,7 +275,7 @@ func (input *BeegoInput) Param(key string) string { // SetParam will set the param with key and value func (input *BeegoInput) SetParam(key, val string) { - input.pvalues[len(input.pnames)] = val + input.pvalues = append(input.pvalues, val) input.pnames = append(input.pnames, key) } diff --git a/tree.go b/tree.go index dbcc63c9..2092016f 100644 --- a/tree.go +++ b/tree.go @@ -288,7 +288,8 @@ func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{ if len(pattern) == 0 || pattern[0] != '/' { return nil } - return t.match(pattern, nil, ctx) + w := make([]string, 0, 20) + return t.match(pattern, w, ctx) } func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) {