mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-13 13:12:17 +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"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/internal/bytesconv"
|
"github.com/gin-gonic/gin/internal/bytesconv"
|
||||||
filesystem "github.com/gin-gonic/gin/internal/fs"
|
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.
|
// HandlerFunc defines the handler used by gin middleware as return value.
|
||||||
type HandlerFunc func(*Context)
|
type HandlerFunc func(*Context)
|
||||||
|
|
||||||
@ -776,8 +771,8 @@ func redirectTrailingSlash(c *Context) {
|
|||||||
req := c.Request
|
req := c.Request
|
||||||
p := req.URL.Path
|
p := req.URL.Path
|
||||||
if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
|
if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
|
||||||
prefix = regSafePrefix.ReplaceAllString(prefix, "")
|
prefix = sanitizePathChars(prefix)
|
||||||
prefix = regRemoveRepeatedChar.ReplaceAllString(prefix, "/")
|
prefix = removeRepeatedSlash(prefix)
|
||||||
|
|
||||||
p = prefix + "/" + req.URL.Path
|
p = prefix + "/" + req.URL.Path
|
||||||
}
|
}
|
||||||
@ -788,6 +783,39 @@ func redirectTrailingSlash(c *Context) {
|
|||||||
redirectRequest(c)
|
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 {
|
func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
|
||||||
req := c.Request
|
req := c.Request
|
||||||
rPath := req.URL.Path
|
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())
|
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