mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-11 19:47:00 +08:00
perf: replace regex with custom functions in redirectTrailingSlash
This commit is contained in:
parent
ecb3f7b5e2
commit
95504d9ec7
44
gin.go
44
gin.go
@ -11,9 +11,9 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"github.com/gin-gonic/gin/internal/bytesconv"
|
||||
filesystem "github.com/gin-gonic/gin/internal/fs"
|
||||
@ -48,11 +48,6 @@ var defaultTrustedCIDRs = []*net.IPNet{
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
regSafePrefix = regexp.MustCompile("[^a-zA-Z0-9/-]+")
|
||||
regRemoveRepeatedChar = regexp.MustCompile("/{2,}")
|
||||
)
|
||||
|
||||
// HandlerFunc defines the handler used by gin middleware as return value.
|
||||
type HandlerFunc func(*Context)
|
||||
|
||||
@ -776,8 +771,8 @@ func redirectTrailingSlash(c *Context) {
|
||||
req := c.Request
|
||||
p := req.URL.Path
|
||||
if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
|
||||
prefix = regSafePrefix.ReplaceAllString(prefix, "")
|
||||
prefix = regRemoveRepeatedChar.ReplaceAllString(prefix, "/")
|
||||
prefix = sanitizePathChars(prefix)
|
||||
prefix = removeRepeatedSlash(prefix)
|
||||
|
||||
p = prefix + "/" + req.URL.Path
|
||||
}
|
||||
@ -788,6 +783,39 @@ func redirectTrailingSlash(c *Context) {
|
||||
redirectRequest(c)
|
||||
}
|
||||
|
||||
// sanitizePathChars removes unsafe characters from path strings,
|
||||
// keeping only letters, numbers, forward slashes, and hyphens.
|
||||
func sanitizePathChars(s string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '/' || r == '-' {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}, s)
|
||||
}
|
||||
|
||||
// removeRepeatedSlash removes consecutive forward slashes from a string,
|
||||
// replacing sequences of multiple slashes with a single slash.
|
||||
func removeRepeatedSlash(s string) string {
|
||||
if !strings.Contains(s, "//") {
|
||||
return s
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(s) - 1)
|
||||
prevChar := rune(0)
|
||||
|
||||
for _, r := range s {
|
||||
if r == '/' && prevChar == '/' {
|
||||
continue
|
||||
}
|
||||
sb.WriteRune(r)
|
||||
prevChar = r
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
|
||||
req := c.Request
|
||||
rPath := req.URL.Path
|
||||
|
||||
31
gin_test.go
31
gin_test.go
@ -1012,3 +1012,34 @@ func TestUpdateRouteTreesCalledOnce(t *testing.T) {
|
||||
assert.Equal(t, "ok", w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveRepeatedSlash(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
str string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "noSlash",
|
||||
str: "abc",
|
||||
want: "abc",
|
||||
},
|
||||
{
|
||||
name: "withSlash",
|
||||
str: "/a/b/c/",
|
||||
want: "/a/b/c/",
|
||||
},
|
||||
{
|
||||
name: "withRepeatedSlash",
|
||||
str: "/a//b///c////",
|
||||
want: "/a/b/c/",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res := removeRepeatedSlash(tc.str)
|
||||
assert.Equal(t, tc.want, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user