From 2d0455aa3b988d16b105560ff1cd1207a8b92b97 Mon Sep 17 00:00:00 2001 From: Gates Wang <9372086+SetagGnaw@users.noreply.github.com> Date: Sun, 2 Aug 2026 19:18:08 -0400 Subject: [PATCH] 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. --- gin.go | 1 + routes_test.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/gin.go b/gin.go index 2e033bf3..434b1651 100644 --- a/gin.go +++ b/gin.go @@ -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) } diff --git a/routes_test.go b/routes_test.go index 1cae3fce..4e85db40 100644 --- a/routes_test.go +++ b/routes_test.go @@ -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