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,
response: res,
echo: e,
pnames: make([]string, e.maxParam),
pvalues: make([]string, e.maxParam),
pvalues: make([]string, *e.maxParam),
store: make(store),
}
}

12
echo.go
View File

@ -25,7 +25,7 @@ type (
prefix string
middleware []MiddlewareFunc
http2 bool
maxParam byte
maxParam *int
notFoundHandler HandlerFunc
defaultHTTPErrorHandler HTTPErrorHandler
httpErrorHandler HTTPErrorHandler
@ -141,8 +141,7 @@ var (
// New creates an Echo instance.
func New() (e *Echo) {
e = &Echo{}
e.router = NewRouter(e)
e = &Echo{maxParam: new(int), router: NewRouter(e)}
e.pool.New = func() interface{} {
return NewContext(nil, new(Response), e)
}
@ -152,7 +151,6 @@ func New() (e *Echo) {
//----------
e.HTTP2(true)
e.SetMaxParam(5)
e.notFoundHandler = func(c *Context) error {
return NewHTTPError(http.StatusNotFound)
}
@ -192,12 +190,6 @@ func (e *Echo) HTTP2(on bool) {
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.
func (e *Echo) DefaultHTTPErrorHandler(err error, c *Context) {
e.defaultHTTPErrorHandler(err, c)

View File

@ -41,13 +41,6 @@ func TestEcho(t *testing.T) {
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) {
e := New()
e.Index("examples/website/public/index.html")

View File

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

View File

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