Skip to content

Commit 03ce9b2

Browse files
authored
Merge pull request #1661 from pafuent/fix_router_find_after_invalid_set_param_values
Fixed Router#Find panic an infinite loop
2 parents cf00202 + 53653b3 commit 03ce9b2

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

context.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,35 @@ func (c *context) ParamNames() []string {
314314

315315
func (c *context) SetParamNames(names ...string) {
316316
c.pnames = names
317-
*c.echo.maxParam = len(names)
317+
318+
l := len(names)
319+
if *c.echo.maxParam < l {
320+
*c.echo.maxParam = l
321+
}
322+
323+
if len(c.pvalues) < l {
324+
// Keeping the old pvalues just for backward compatibility, but it sounds that doesn't make sense to keep them,
325+
// probably those values will be overriden in a Context#SetParamValues
326+
newPvalues := make([]string, l)
327+
copy(newPvalues, c.pvalues)
328+
c.pvalues = newPvalues
329+
}
318330
}
319331

320332
func (c *context) ParamValues() []string {
321333
return c.pvalues[:len(c.pnames)]
322334
}
323335

324336
func (c *context) SetParamValues(values ...string) {
325-
c.pvalues = values
337+
// NOTE: Don't just set c.pvalues = values, because it has to have length c.echo.maxParam at all times
338+
// It will brake the Router#Find code
339+
limit := len(values)
340+
if limit > *c.echo.maxParam {
341+
limit = *c.echo.maxParam
342+
}
343+
for i := 0; i < limit; i++ {
344+
c.pvalues[i] = values[i]
345+
}
326346
}
327347

328348
func (c *context) QueryParam(name string) string {

context_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,40 @@ func TestContextGetAndSetParam(t *testing.T) {
517517
})
518518
}
519519

520+
// Issue #1655
521+
func TestContextSetParamNamesShouldUpdateEchoMaxParam(t *testing.T) {
522+
assert := testify.New(t)
523+
524+
e := New()
525+
assert.Equal(0, *e.maxParam)
526+
527+
expectedOneParam := []string{"one"}
528+
expectedTwoParams := []string{"one", "two"}
529+
expectedThreeParams := []string{"one", "two", ""}
530+
expectedABCParams := []string{"A", "B", "C"}
531+
532+
c := e.NewContext(nil, nil)
533+
c.SetParamNames("1", "2")
534+
c.SetParamValues(expectedTwoParams...)
535+
assert.Equal(2, *e.maxParam)
536+
assert.EqualValues(expectedTwoParams, c.ParamValues())
537+
538+
c.SetParamNames("1")
539+
assert.Equal(2, *e.maxParam)
540+
// Here for backward compatibility the ParamValues remains as they are
541+
assert.EqualValues(expectedOneParam, c.ParamValues())
542+
543+
c.SetParamNames("1", "2", "3")
544+
assert.Equal(3, *e.maxParam)
545+
// Here for backward compatibility the ParamValues remains as they are, but the len is extended to e.maxParam
546+
assert.EqualValues(expectedThreeParams, c.ParamValues())
547+
548+
c.SetParamValues("A", "B", "C", "D")
549+
assert.Equal(3, *e.maxParam)
550+
// Here D shouldn't be returned
551+
assert.EqualValues(expectedABCParams, c.ParamValues())
552+
}
553+
520554
func TestContextFormValue(t *testing.T) {
521555
f := make(url.Values)
522556
f.Set("name", "Jon Snow")

router_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,43 @@ func TestRouterParam1466(t *testing.T) {
14301430
assert.Equal(t, 0, c.response.Status)
14311431
}
14321432

1433+
// Issue #1655
1434+
func TestRouterFindNotPanicOrLoopsWhenContextSetParamValuesIsCalledWithLessValuesThanEchoMaxParam(t *testing.T) {
1435+
e := New()
1436+
r := e.router
1437+
1438+
v0 := e.Group("/:version")
1439+
v0.GET("/admin", func(c Context) error {
1440+
c.SetParamNames("version")
1441+
c.SetParamValues("v1")
1442+
return nil
1443+
})
1444+
1445+
v0.GET("/images/view/:id", handlerHelper("iv", 1))
1446+
v0.GET("/images/:id", handlerHelper("i", 1))
1447+
v0.GET("/view/*", handlerHelper("v", 1))
1448+
1449+
//If this API is called before the next two one panic the other loops ( of course without my fix ;) )
1450+
c := e.NewContext(nil, nil)
1451+
r.Find(http.MethodGet, "/v1/admin", c)
1452+
c.Handler()(c)
1453+
assert.Equal(t, "v1", c.Param("version"))
1454+
1455+
//panic
1456+
c = e.NewContext(nil, nil)
1457+
r.Find(http.MethodGet, "/v1/view/same-data", c)
1458+
c.Handler()(c)
1459+
assert.Equal(t, "same-data", c.Param("*"))
1460+
assert.Equal(t, 1, c.Get("v"))
1461+
1462+
//looping
1463+
c = e.NewContext(nil, nil)
1464+
r.Find(http.MethodGet, "/v1/images/view", c)
1465+
c.Handler()(c)
1466+
assert.Equal(t, "view", c.Param("id"))
1467+
assert.Equal(t, 1, c.Get("i"))
1468+
}
1469+
14331470
// Issue #1653
14341471
func TestRouterPanicWhenParamNoRootOnlyChildsFailsFind(t *testing.T) {
14351472
e := New()

0 commit comments

Comments
 (0)