1
0
mirror of https://github.com/labstack/echo.git synced 2024-12-24 20:14:31 +02:00

Not need of Echo.SetMaxParam

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2015-06-03 15:18:27 -07:00
parent 1a5e2935ea
commit e7c1d5d9fb
6 changed files with 140 additions and 135 deletions

View File

@ -28,8 +28,7 @@ func NewContext(req *http.Request, res *Response, e *Echo) *Context {
request: req, request: req,
response: res, response: res,
echo: e, echo: e,
pnames: make([]string, e.maxParam), pvalues: make([]string, *e.maxParam),
pvalues: make([]string, e.maxParam),
store: make(store), store: make(store),
} }
} }

12
echo.go
View File

@ -25,7 +25,7 @@ type (
prefix string prefix string
middleware []MiddlewareFunc middleware []MiddlewareFunc
http2 bool http2 bool
maxParam byte maxParam *int
notFoundHandler HandlerFunc notFoundHandler HandlerFunc
defaultHTTPErrorHandler HTTPErrorHandler defaultHTTPErrorHandler HTTPErrorHandler
httpErrorHandler HTTPErrorHandler httpErrorHandler HTTPErrorHandler
@ -141,8 +141,7 @@ var (
// New creates an Echo instance. // New creates an Echo instance.
func New() (e *Echo) { func New() (e *Echo) {
e = &Echo{} e = &Echo{maxParam: new(int), router: NewRouter(e)}
e.router = NewRouter(e)
e.pool.New = func() interface{} { e.pool.New = func() interface{} {
return NewContext(nil, new(Response), e) return NewContext(nil, new(Response), e)
} }
@ -152,7 +151,6 @@ func New() (e *Echo) {
//---------- //----------
e.HTTP2(true) e.HTTP2(true)
e.SetMaxParam(5)
e.notFoundHandler = func(c *Context) error { e.notFoundHandler = func(c *Context) error {
return NewHTTPError(http.StatusNotFound) return NewHTTPError(http.StatusNotFound)
} }
@ -192,12 +190,6 @@ func (e *Echo) HTTP2(on bool) {
e.http2 = on e.http2 = on
} }
// SetMaxParam sets the maximum number of path parameters allowed for the application.
// Default value is 5, good enough for many use cases.
func (e *Echo) SetMaxParam(n uint8) {
e.maxParam = n
}
// DefaultHTTPErrorHandler invokes the default HTTP error handler. // DefaultHTTPErrorHandler invokes the default HTTP error handler.
func (e *Echo) DefaultHTTPErrorHandler(err error, c *Context) { func (e *Echo) DefaultHTTPErrorHandler(err error, c *Context) {
e.defaultHTTPErrorHandler(err, c) e.defaultHTTPErrorHandler(err, c)

View File

@ -41,13 +41,6 @@ func TestEcho(t *testing.T) {
assert.Equal(t, http.StatusInternalServerError, rec.Code) assert.Equal(t, http.StatusInternalServerError, rec.Code)
} }
// TODO: Improve me!
func TestEchoMaxParam(t *testing.T) {
e := New()
e.SetMaxParam(8)
assert.EqualValues(t, 8, e.maxParam)
}
func TestEchoIndex(t *testing.T) { func TestEchoIndex(t *testing.T) {
e := New() e := New()
e.Index("examples/website/public/index.html") e.Index("examples/website/public/index.html")

View File

@ -4,9 +4,9 @@ import "net/http"
type ( type (
Router struct { Router struct {
trees map[string]*node trees map[string]*node
routes []Route routes []Route
echo *Echo echo *Echo
} }
node struct { node struct {
typ ntype typ ntype
@ -43,14 +43,14 @@ func NewRouter(e *Echo) (r *Router) {
return return
} }
func (r *Router) Add(method, path string, h HandlerFunc, echo *Echo) { func (r *Router) Add(method, path string, h HandlerFunc, e *Echo) {
var pnames []string // Param names pnames := []string{} // Param names
for i, l := 0, len(path); i < l; i++ { for i, l := 0, len(path); i < l; i++ {
if path[i] == ':' { if path[i] == ':' {
j := i + 1 j := i + 1
r.insert(method, path[:i], nil, stype, nil, echo) r.insert(method, path[:i], nil, stype, nil, e)
for ; i < l && path[i] != '/'; i++ { for ; i < l && path[i] != '/'; i++ {
} }
@ -59,21 +59,28 @@ func (r *Router) Add(method, path string, h HandlerFunc, echo *Echo) {
i, l = j, len(path) i, l = j, len(path)
if i == l { if i == l {
r.insert(method, path[:i], h, ptype, pnames, echo) r.insert(method, path[:i], h, ptype, pnames, e)
return return
} }
r.insert(method, path[:i], nil, ptype, pnames, echo) r.insert(method, path[:i], nil, ptype, pnames, e)
} else if path[i] == '*' { } else if path[i] == '*' {
r.insert(method, path[:i], nil, stype, nil, echo) r.insert(method, path[:i], nil, stype, nil, e)
pnames = append(pnames, "_name") pnames = append(pnames, "_name")
r.insert(method, path[:i+1], h, mtype, pnames, echo) r.insert(method, path[:i+1], h, mtype, pnames, e)
return return
} }
} }
r.insert(method, path, h, stype, pnames, echo)
r.insert(method, path, h, stype, pnames, e)
} }
func (r *Router) insert(method, path string, h HandlerFunc, t ntype, pnames []string, echo *Echo) { func (r *Router) insert(method, path string, h HandlerFunc, t ntype, pnames []string, e *Echo) {
// Adjust max param
l := len(pnames)
if *e.maxParam < l {
*e.maxParam = l
}
cn := r.trees[method] // Current node as root cn := r.trees[method] // Current node as root
search := path search := path
@ -90,7 +97,7 @@ func (r *Router) insert(method, path string, h HandlerFunc, t ntype, pnames []st
cn.typ = t cn.typ = t
cn.handler = h cn.handler = h
cn.pnames = pnames cn.pnames = pnames
cn.echo = echo cn.echo = e
} }
} else if l < pl { } else if l < pl {
// Split node // Split node
@ -110,10 +117,10 @@ func (r *Router) insert(method, path string, h HandlerFunc, t ntype, pnames []st
cn.typ = t cn.typ = t
cn.handler = h cn.handler = h
cn.pnames = pnames cn.pnames = pnames
cn.echo = echo cn.echo = e
} else { } else {
// Create child node // Create child node
n = newNode(t, search[l:], cn, nil, h, pnames, echo) n = newNode(t, search[l:], cn, nil, h, pnames, e)
cn.children = append(cn.children, n) cn.children = append(cn.children, n)
} }
} else if l < sl { } else if l < sl {
@ -125,21 +132,21 @@ func (r *Router) insert(method, path string, h HandlerFunc, t ntype, pnames []st
continue continue
} }
// Create child node // Create child node
n := newNode(t, search, cn, nil, h, pnames, echo) n := newNode(t, search, cn, nil, h, pnames, e)
cn.children = append(cn.children, n) cn.children = append(cn.children, n)
} else { } else {
// Node already exists // Node already exists
if h != nil { if h != nil {
cn.handler = h cn.handler = h
cn.pnames = pnames cn.pnames = pnames
cn.echo = echo cn.echo = e
} }
} }
return return
} }
} }
func newNode(t ntype, pre string, p *node, c children, h HandlerFunc, pnames []string, echo *Echo) *node { func newNode(t ntype, pre string, p *node, c children, h HandlerFunc, pnames []string, e *Echo) *node {
return &node{ return &node{
typ: t, typ: t,
label: pre[0], label: pre[0],
@ -148,7 +155,7 @@ func newNode(t ntype, pre string, p *node, c children, h HandlerFunc, pnames []s
children: c, children: c,
handler: h, handler: h,
pnames: pnames, pnames: pnames,
echo: echo, echo: e,
} }
} }
@ -200,7 +207,7 @@ func lcp(a, b string) (i int) {
return return
} }
func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, echo *Echo) { func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo) {
cn := r.trees[method] // Current node as root cn := r.trees[method] // Current node as root
search := path search := path
@ -220,7 +227,7 @@ func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, echo *E
// Found // Found
ctx.pnames = cn.pnames ctx.pnames = cn.pnames
h = cn.handler h = cn.handler
echo = cn.echo e = cn.echo
return return
} }

View File

@ -10,7 +10,6 @@ import (
) )
var ( var (
context = NewContext(nil, nil, New())
api = []Route{ api = []Route{
// OAuth Authorizations // OAuth Authorizations
{"GET", "/authorizations", nil}, {"GET", "/authorizations", nil},
@ -276,230 +275,248 @@ var (
) )
func TestRouterStatic(t *testing.T) { func TestRouterStatic(t *testing.T) {
r := New().router e := New()
r := e.router
path := "/folders/a/files/echo.gif" path := "/folders/a/files/echo.gif"
r.Add(GET, path, func(c *Context) error { r.Add(GET, path, func(c *Context) error {
c.Set("path", path) c.Set("path", path)
return nil return nil
}, nil) }, e)
h, _ := r.Find(GET, path, context) c := NewContext(nil, nil, e)
h, _ := r.Find(GET, path, c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, path, context.Get("path")) assert.Equal(t, path, c.Get("path"))
} }
} }
func TestRouterParam(t *testing.T) { func TestRouterParam(t *testing.T) {
r := New().router e := New()
r := e.router
r.Add(GET, "/users/:id", func(c *Context) error { r.Add(GET, "/users/:id", func(c *Context) error {
return nil return nil
}, nil) }, e)
h, _ := r.Find(GET, "/users/1", context) c := NewContext(nil, nil, e)
h, _ := r.Find(GET, "/users/1", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
assert.Equal(t, "1", context.P(0)) assert.Equal(t, "1", c.P(0))
} }
} }
func TestRouterTwoParam(t *testing.T) { func TestRouterTwoParam(t *testing.T) {
r := New().router e := New()
r := e.router
r.Add(GET, "/users/:uid/files/:fid", func(*Context) error { r.Add(GET, "/users/:uid/files/:fid", func(*Context) error {
return nil return nil
}, nil) }, e)
c := NewContext(nil, nil, e)
h, _ := r.Find(GET, "/users/1/files/1", context) h, _ := r.Find(GET, "/users/1/files/1", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
assert.Equal(t, "1", context.P(0)) assert.Equal(t, "1", c.P(0))
assert.Equal(t, "1", context.P(1)) assert.Equal(t, "1", c.P(1))
} }
h, _ = r.Find(GET, "/users/1", context) h, _ = r.Find(GET, "/users/1", c)
assert.Nil(t, h) assert.Nil(t, h)
} }
func TestRouterMatchAny(t *testing.T) { func TestRouterMatchAny(t *testing.T) {
r := New().router e := New()
r := e.router
r.Add(GET, "/users/*", func(*Context) error { r.Add(GET, "/users/*", func(*Context) error {
return nil return nil
}, nil) }, e)
c := NewContext(nil, nil, e)
h, _ := r.Find(GET, "/users/", context) h, _ := r.Find(GET, "/users/", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
assert.Equal(t, "", context.P(0)) assert.Equal(t, "", c.P(0))
} }
h, _ = r.Find(GET, "/users/1", context) h, _ = r.Find(GET, "/users/1", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
assert.Equal(t, "1", context.P(0)) assert.Equal(t, "1", c.P(0))
} }
} }
func TestRouterMicroParam(t *testing.T) { func TestRouterMicroParam(t *testing.T) {
r := New().router e := New()
r := e.router
r.Add(GET, "/:a/:b/:c", func(c *Context) error { r.Add(GET, "/:a/:b/:c", func(c *Context) error {
return nil return nil
}, nil) }, e)
h, _ := r.Find(GET, "/1/2/3", context) c := NewContext(nil, nil, e)
h, _ := r.Find(GET, "/1/2/3", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
assert.Equal(t, "1", context.P(0)) assert.Equal(t, "1", c.P(0))
assert.Equal(t, "2", context.P(1)) assert.Equal(t, "2", c.P(1))
assert.Equal(t, "3", context.P(2)) assert.Equal(t, "3", c.P(2))
} }
} }
func TestRouterMultiRoute(t *testing.T) { func TestRouterMultiRoute(t *testing.T) {
r := New().router e := New()
r := e.router
// Routes // Routes
r.Add(GET, "/users", func(c *Context) error { r.Add(GET, "/users", func(c *Context) error {
c.Set("path", "/users") c.Set("path", "/users")
return nil return nil
}, nil) }, e)
r.Add(GET, "/users/:id", func(c *Context) error { r.Add(GET, "/users/:id", func(c *Context) error {
return nil return nil
}, nil) }, e)
c := NewContext(nil, nil, e)
// Route > /users // Route > /users
h, _ := r.Find(GET, "/users", context) h, _ := r.Find(GET, "/users", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, "/users", context.Get("path")) assert.Equal(t, "/users", c.Get("path"))
} }
// Route > /users/:id // Route > /users/:id
h, _ = r.Find(GET, "/users/1", context) h, _ = r.Find(GET, "/users/1", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
assert.Equal(t, "1", context.P(0)) assert.Equal(t, "1", c.P(0))
} }
// Route > /user // Route > /user
h, _ = r.Find(GET, "/user", context) h, _ = r.Find(GET, "/user", c)
assert.Nil(t, h) assert.Nil(t, h)
} }
func TestRouterPriority(t *testing.T) { func TestRouterPriority(t *testing.T) {
r := New().router e := New()
r := e.router
// Routes // Routes
r.Add(GET, "/users", func(c *Context) error { r.Add(GET, "/users", func(c *Context) error {
c.Set("a", 1) c.Set("a", 1)
return nil return nil
}, nil) }, e)
r.Add(GET, "/users/new", func(c *Context) error { r.Add(GET, "/users/new", func(c *Context) error {
c.Set("b", 2) c.Set("b", 2)
return nil return nil
}, nil) }, e)
r.Add(GET, "/users/:id", func(c *Context) error { r.Add(GET, "/users/:id", func(c *Context) error {
c.Set("c", 3) c.Set("c", 3)
return nil return nil
}, nil) }, e)
r.Add(GET, "/users/dew", func(c *Context) error { r.Add(GET, "/users/dew", func(c *Context) error {
c.Set("d", 4) c.Set("d", 4)
return nil return nil
}, nil) }, e)
r.Add(GET, "/users/:id/files", func(c *Context) error { r.Add(GET, "/users/:id/files", func(c *Context) error {
c.Set("e", 5) c.Set("e", 5)
return nil return nil
}, nil) }, e)
r.Add(GET, "/users/newsee", func(c *Context) error { r.Add(GET, "/users/newsee", func(c *Context) error {
c.Set("f", 6) c.Set("f", 6)
return nil return nil
}, nil) }, e)
r.Add(GET, "/users/*", func(c *Context) error { r.Add(GET, "/users/*", func(c *Context) error {
c.Set("g", 7) c.Set("g", 7)
return nil return nil
}, nil) }, e)
c := NewContext(nil, nil, e)
// Route > /users // Route > /users
h, _ := r.Find(GET, "/users", context) h, _ := r.Find(GET, "/users", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, 1, context.Get("a")) assert.Equal(t, 1, c.Get("a"))
} }
// Route > /users/new // Route > /users/new
h, _ = r.Find(GET, "/users/new", context) h, _ = r.Find(GET, "/users/new", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, 2, context.Get("b")) assert.Equal(t, 2, c.Get("b"))
} }
// Route > /users/:id // Route > /users/:id
h, _ = r.Find(GET, "/users/1", context) h, _ = r.Find(GET, "/users/1", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, 3, context.Get("c")) assert.Equal(t, 3, c.Get("c"))
} }
// Route > /users/dew // Route > /users/dew
h, _ = r.Find(GET, "/users/dew", context) h, _ = r.Find(GET, "/users/dew", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, 4, context.Get("d")) assert.Equal(t, 4, c.Get("d"))
} }
// Route > /users/:id/files // Route > /users/:id/files
h, _ = r.Find(GET, "/users/1/files", context) h, _ = r.Find(GET, "/users/1/files", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, 5, context.Get("e")) assert.Equal(t, 5, c.Get("e"))
} }
// Route > /users/:id // Route > /users/:id
h, _ = r.Find(GET, "/users/news", context) h, _ = r.Find(GET, "/users/news", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, 3, context.Get("c")) assert.Equal(t, 3, c.Get("c"))
} }
// Route > /users/* // Route > /users/*
h, _ = r.Find(GET, "/users/joe/books", context) h, _ = r.Find(GET, "/users/joe/books", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, 7, context.Get("g")) assert.Equal(t, 7, c.Get("g"))
} }
} }
func TestRouterParamNames(t *testing.T) { func TestRouterParamNames(t *testing.T) {
r := New().router e := New()
r := e.router
// Routes // Routes
r.Add(GET, "/users", func(c *Context) error { r.Add(GET, "/users", func(c *Context) error {
c.Set("path", "/users") c.Set("path", "/users")
return nil return nil
}, nil) }, e)
r.Add(GET, "/users/:id", func(c *Context) error { r.Add(GET, "/users/:id", func(c *Context) error {
return nil return nil
}, nil) }, e)
r.Add(GET, "/users/:uid/files/:fid", func(c *Context) error { r.Add(GET, "/users/:uid/files/:fid", func(c *Context) error {
return nil return nil
}, nil) }, e)
c := NewContext(nil, nil, e)
// Route > /users // Route > /users
h, _ := r.Find(GET, "/users", context) h, _ := r.Find(GET, "/users", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
assert.Equal(t, "/users", context.Get("path")) assert.Equal(t, "/users", c.Get("path"))
} }
// Route > /users/:id // Route > /users/:id
h, _ = r.Find(GET, "/users/1", context) h, _ = r.Find(GET, "/users/1", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
assert.Equal(t, "id", context.pnames[0]) assert.Equal(t, "id", c.pnames[0])
assert.Equal(t, "1", context.P(0)) assert.Equal(t, "1", c.P(0))
} }
// Route > /users/:uid/files/:fid // Route > /users/:uid/files/:fid
h, _ = r.Find(GET, "/users/1/files/1", context) h, _ = r.Find(GET, "/users/1/files/1", c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
assert.Equal(t, "uid", context.pnames[0]) assert.Equal(t, "uid", c.pnames[0])
assert.Equal(t, "1", context.P(0)) assert.Equal(t, "1", c.P(0))
assert.Equal(t, "fid", context.pnames[1]) assert.Equal(t, "fid", c.pnames[1])
assert.Equal(t, "1", context.P(1)) assert.Equal(t, "1", c.P(1))
} }
} }
func TestRouterAPI(t *testing.T) { func TestRouterAPI(t *testing.T) {
r := New().router e := New()
r := e.router
for _, route := range api { for _, route := range api {
r.Add(route.Method, route.Path, func(c *Context) error { r.Add(route.Method, route.Path, func(c *Context) error {
for i, n := range c.pnames { for i, n := range c.pnames {
@ -508,19 +525,24 @@ func TestRouterAPI(t *testing.T) {
} }
} }
return nil return nil
}, nil) }, e)
h, _ := r.Find(route.Method, route.Path, context) }
c := NewContext(nil, nil, e)
for _, route := range api {
h, _ := r.Find(route.Method, route.Path, c)
if assert.NotNil(t, h) { if assert.NotNil(t, h) {
h(context) h(c)
} }
} }
} }
func TestRouterServeHTTP(t *testing.T) { func TestRouterServeHTTP(t *testing.T) {
r := New().router e := New()
r := e.router
r.Add(GET, "/users", func(*Context) error { r.Add(GET, "/users", func(*Context) error {
return nil return nil
}, nil) }, e)
// OK // OK
req, _ := http.NewRequest(GET, "/users", nil) req, _ := http.NewRequest(GET, "/users", nil)

View File

@ -27,14 +27,6 @@ Specific version of Echo can be installed using any [package manager](https://gi
## Customization ## Customization
### Max path parameters
`Echo.SetMaxParam(n uint8)`
Sets the maximum number of path parameters allowed for the application.
Default value is **5**, [good enough](https://github.com/interagent/http-api-design#minimize-path-nesting)
for many use cases. Restricting path parameters allows us to use memory efficiently.
### HTTP error handler ### HTTP error handler
`Echo.SetHTTPErrorHandler(h HTTPErrorHandler)` `Echo.SetHTTPErrorHandler(h HTTPErrorHandler)`