A `/:name` route does not match `/b` when a static `/b/` route is also registered
Description
With the routes /b/ and /:name registered, GET /b is not served by /:name: it is a 404, or with RedirectTrailingSlash (the default) a 301 to /b/. The mirror shape does the same: with /a/b and /:p/b/, GET /a/b/ is a 404. GET /c is served by /:name as expected, and a static route /b/x beside /:name does not stop GET /b from matching /:name; a static route that differs from the request by only a trailing slash does.
Gin Version
v1.12.0, and master at 3b08cd7 (same output).
Can you reproduce the bug?
Yes
Source Code
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/gin-gonic/gin"
)
func get(routes []string, path string, redirectTrailingSlash bool) {
r := gin.New()
r.RedirectTrailingSlash = redirectTrailingSlash
for _, route := range routes {
r.GET(route, func(c *gin.Context) { c.String(200, "%s %v", route, c.Params) })
}
w := httptest.NewRecorder()
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, path, nil))
fmt.Printf("routes %v, GET %s: %d %q Location=%q\n", routes, path, w.Code, w.Body.String(), w.Header().Get("Location"))
}
func main() {
gin.SetMode(gin.ReleaseMode)
get([]string{"/b/", "/:name"}, "/b", false)
get([]string{"/a/b", "/:p/b/"}, "/a/b/", false)
get([]string{"/b/", "/:name"}, "/c", false)
get([]string{"/b/x", "/:name"}, "/b", false)
get([]string{"/b/", "/:name"}, "/b", true)
}Output:
routes [/b/ /:name], GET /b: 404 "404 page not found" Location=""
routes [/a/b /:p/b/], GET /a/b/: 404 "404 page not found" Location=""
routes [/b/ /:name], GET /c: 200 "/:name [{name c}]" Location=""
routes [/b/x /:name], GET /b: 200 "/:name [{name b}]" Location=""
routes [/b/ /:name], GET /b: 301 "<a href=\"/b/\">Moved Permanently</a>.\n\n" Location="/b/"I expected the first two requests to be served like the third and fourth, with name = "b" and p = "a", and the last one to be served by /:name rather than redirected to the /b/ handler, since a route matching the request as sent exists.
Go Version
go1.27.1
Operating System
macOS (darwin/arm64); the requests above are served through httptest, nothing OS-specific.
BTW, this was found by an automated program that writes property-based tests for various open source projects using hegel (but it has been reviewed by hand before reporting). We've also potentially found (but not yet hand validated) 10 other bugs in gin. You can see the tests at https://github.com/hegeldev/hegel-zoo/tree/main/targets/go/gin. Let us know if you would like us to file the other bugs found and/or contribute the tests. NB the tests are currently LLM generated and probably not yet suitable for inclusion as is, but we're happy to help get them into a better state if you want them.
Source: gin-gonic/gin