fix(gin): reset skippedNodes between method tree probes in 405 handling

handleHTTPRequest reused c.skippedNodes across the per-method getValue
probes that build the Allow header when HandleMethodNotAllowed is
enabled. A probe that matched via a static branch left its skipped
wildcard entries on the stack, and when a later tree's probe
dead-ended it could roll back through such a stale entry into the
previous tree's nodes, find a handler there, and wrongly list its own
method in the Allow header. Reset the stack before each probe.
This commit is contained in:
Gates Wang 2026-08-02 19:18:08 -04:00
parent 1e39af4f58
commit 2d0455aa3b
2 changed files with 18 additions and 0 deletions

1
gin.go
View File

@ -743,6 +743,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
if tree.method == httpMethod {
continue
}
*c.skippedNodes = (*c.skippedNodes)[:0]
if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
allowed = append(allowed, tree.method)
}

View File

@ -527,6 +527,23 @@ func TestRouteNotAllowedEnabled3(t *testing.T) {
assert.Contains(t, allowed, http.MethodPost)
}
func TestRouteNotAllowedDoesNotReuseSkippedNodes(t *testing.T) {
router := New()
router.HandleMethodNotAllowed = true
router.POST("/b", func(c *Context) {})
router.POST("/:p0", func(c *Context) {})
router.PUT("/a/:p1", func(c *Context) {})
// The POST probe for the Allow header matches static "/b" while
// recording the skipped wildcard ":p0". handleHTTPRequest must reset
// c.skippedNodes before probing the PUT tree, or the stale entry
// leaks the POST route into the PUT lookup and PUT is wrongly
// reported in the Allow header.
w := PerformRequest(router, http.MethodGet, "/b")
assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
assert.Equal(t, http.MethodPost, w.Header().Get("Allow"))
}
func TestRouteNotAllowedDisabled(t *testing.T) {
router := New()
router.HandleMethodNotAllowed = false