mirror of
https://github.com/gin-gonic/gin.git
synced 2026-06-12 00:28:37 +08:00
Compare commits
7 Commits
4c8afb96c9
...
4515852aab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4515852aab | ||
|
|
e3118cc378 | ||
|
|
cad29c5e3f | ||
|
|
96f63d68d3 | ||
|
|
5f94d6f3e8 | ||
|
|
51ef7a6a10 | ||
|
|
22b88e0ed1 |
@ -154,7 +154,7 @@ func runRequest(B *testing.B, r *Engine, method, path string) {
|
||||
w := newMockWriter()
|
||||
B.ReportAllocs()
|
||||
B.ResetTimer()
|
||||
for i := 0; i < B.N; i++ {
|
||||
for B.Loop() {
|
||||
r.ServeHTTP(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ func (err SliceValidationError) Error() string {
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
for i := 0; i < len(err); i++ {
|
||||
for i := range len(err) {
|
||||
if err[i] != nil {
|
||||
if b.Len() > 0 {
|
||||
b.WriteString("\n")
|
||||
@ -58,7 +58,7 @@ func (v *defaultValidator) ValidateStruct(obj any) error {
|
||||
case reflect.Slice, reflect.Array:
|
||||
count := value.Len()
|
||||
validateRet := make(SliceValidationError, 0)
|
||||
for i := 0; i < count; i++ {
|
||||
for i := range count {
|
||||
if err := v.ValidateStruct(value.Index(i).Interface()); err != nil {
|
||||
validateRet = append(validateRet, err)
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
|
||||
tValue := value.Type()
|
||||
|
||||
var isSet bool
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
for i := range value.NumField() {
|
||||
sf := tValue.Field(i)
|
||||
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
||||
continue
|
||||
|
||||
@ -3677,22 +3677,22 @@ func BenchmarkGetMapFromFormData(b *testing.B) {
|
||||
|
||||
// Test case 3: Large dataset with many bracket keys
|
||||
largeData := make(map[string][]string)
|
||||
for i := 0; i < 100; i++ {
|
||||
for i := range 100 {
|
||||
key := fmt.Sprintf("ids[%d]", i)
|
||||
largeData[key] = []string{fmt.Sprintf("value%d", i)}
|
||||
}
|
||||
for i := 0; i < 50; i++ {
|
||||
for i := range 50 {
|
||||
key := fmt.Sprintf("names[%d]", i)
|
||||
largeData[key] = []string{fmt.Sprintf("name%d", i)}
|
||||
}
|
||||
for i := 0; i < 25; i++ {
|
||||
for i := range 25 {
|
||||
key := fmt.Sprintf("other[key%d]", i)
|
||||
largeData[key] = []string{fmt.Sprintf("other%d", i)}
|
||||
}
|
||||
|
||||
// Test case 4: Dataset with many non-matching keys (worst case)
|
||||
worstCaseData := make(map[string][]string)
|
||||
for i := 0; i < 100; i++ {
|
||||
for i := range 100 {
|
||||
key := fmt.Sprintf("nonmatching%d", i)
|
||||
worstCaseData[key] = []string{fmt.Sprintf("value%d", i)}
|
||||
}
|
||||
@ -3728,7 +3728,7 @@ func BenchmarkGetMapFromFormData(b *testing.B) {
|
||||
for _, bm := range benchmarks {
|
||||
b.Run(bm.name, func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
_, _ = getMapFromFormData(bm.data, bm.key)
|
||||
}
|
||||
})
|
||||
|
||||
22
gin.go
22
gin.go
@ -178,8 +178,10 @@ type Engine struct {
|
||||
FuncMap template.FuncMap
|
||||
allNoRoute HandlersChain
|
||||
allNoMethod HandlersChain
|
||||
allAutoRedirect HandlersChain
|
||||
noRoute HandlersChain
|
||||
noMethod HandlersChain
|
||||
autoRedirect HandlersChain
|
||||
pool sync.Pool
|
||||
trees methodTrees
|
||||
maxParams uint16
|
||||
@ -334,6 +336,13 @@ func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
|
||||
engine.rebuild405Handlers()
|
||||
}
|
||||
|
||||
// AutoRedirect sets the handlers called when auto redirected
|
||||
// (RedirectTrailingSlash and RedirectFixedPath)
|
||||
func (engine *Engine) AutoRedirect(handlers ...HandlerFunc) {
|
||||
engine.autoRedirect = handlers
|
||||
engine.rebuildAutoRedirectHandlers()
|
||||
}
|
||||
|
||||
// Use attaches a global middleware to the router. i.e. the middleware attached through Use() will be
|
||||
// included in the handlers chain for every single request. Even 404, 405, static files...
|
||||
// For example, this is the right place for a logger or error management middleware.
|
||||
@ -341,6 +350,7 @@ func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
|
||||
engine.RouterGroup.Use(middleware...)
|
||||
engine.rebuild404Handlers()
|
||||
engine.rebuild405Handlers()
|
||||
engine.rebuildAutoRedirectHandlers()
|
||||
return engine
|
||||
}
|
||||
|
||||
@ -361,6 +371,10 @@ func (engine *Engine) rebuild405Handlers() {
|
||||
engine.allNoMethod = engine.combineHandlers(engine.noMethod)
|
||||
}
|
||||
|
||||
func (engine *Engine) rebuildAutoRedirectHandlers() {
|
||||
engine.allAutoRedirect = engine.combineHandlers(engine.autoRedirect)
|
||||
}
|
||||
|
||||
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
|
||||
assert1(path[0] == '/', "path must begin with '/'")
|
||||
assert1(method != "", "HTTP method can not be empty")
|
||||
@ -724,6 +738,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
|
||||
return
|
||||
}
|
||||
if httpMethod != http.MethodConnect && rPath != "/" {
|
||||
c.handlers = engine.allAutoRedirect
|
||||
if value.tsr && engine.RedirectTrailingSlash {
|
||||
redirectTrailingSlash(c)
|
||||
return
|
||||
@ -819,13 +834,14 @@ func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
|
||||
|
||||
func redirectRequest(c *Context) {
|
||||
req := c.Request
|
||||
rPath := req.URL.Path
|
||||
rURL := req.URL.String()
|
||||
|
||||
code := http.StatusMovedPermanently // Permanent redirect, request with GET method
|
||||
if req.Method != http.MethodGet {
|
||||
code = http.StatusTemporaryRedirect
|
||||
}
|
||||
c.Next()
|
||||
|
||||
rPath := req.URL.Path
|
||||
rURL := req.URL.String()
|
||||
debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL)
|
||||
http.Redirect(c.Writer, req, rURL, code)
|
||||
c.writermem.WriteHeaderNow()
|
||||
|
||||
@ -400,7 +400,7 @@ func TestConcurrentHandleContext(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
iterations := 200
|
||||
wg.Add(iterations)
|
||||
for i := 0; i < iterations; i++ {
|
||||
for range iterations {
|
||||
go func() {
|
||||
req, err := http.NewRequest(http.MethodGet, "/", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
53
gin_test.go
53
gin_test.go
@ -601,6 +601,59 @@ func TestNoMethodWithGlobalHandlers(t *testing.T) {
|
||||
compareFunc(t, router.allNoMethod[2], middleware0)
|
||||
}
|
||||
|
||||
func TestAutoRedirectWithoutGlobalHandlers(t *testing.T) {
|
||||
var middleware0 HandlerFunc = func(c *Context) {}
|
||||
var middleware1 HandlerFunc = func(c *Context) {}
|
||||
|
||||
router := New()
|
||||
|
||||
router.AutoRedirect(middleware0)
|
||||
assert.Nil(t, router.Handlers)
|
||||
assert.Len(t, router.autoRedirect, 1)
|
||||
assert.Len(t, router.allAutoRedirect, 1)
|
||||
compareFunc(t, router.autoRedirect[0], middleware0)
|
||||
compareFunc(t, router.allAutoRedirect[0], middleware0)
|
||||
|
||||
router.AutoRedirect(middleware1, middleware0)
|
||||
assert.Len(t, router.autoRedirect, 2)
|
||||
assert.Len(t, router.allAutoRedirect, 2)
|
||||
compareFunc(t, router.autoRedirect[0], middleware1)
|
||||
compareFunc(t, router.allAutoRedirect[0], middleware1)
|
||||
compareFunc(t, router.autoRedirect[1], middleware0)
|
||||
compareFunc(t, router.allAutoRedirect[1], middleware0)
|
||||
}
|
||||
|
||||
func TestAutoRedirectWithGlobalHandlers(t *testing.T) {
|
||||
var middleware0 HandlerFunc = func(c *Context) {}
|
||||
var middleware1 HandlerFunc = func(c *Context) {}
|
||||
var middleware2 HandlerFunc = func(c *Context) {}
|
||||
|
||||
router := New()
|
||||
router.Use(middleware2)
|
||||
|
||||
router.AutoRedirect(middleware0)
|
||||
assert.Len(t, router.allAutoRedirect, 2)
|
||||
assert.Len(t, router.Handlers, 1)
|
||||
assert.Len(t, router.autoRedirect, 1)
|
||||
|
||||
compareFunc(t, router.Handlers[0], middleware2)
|
||||
compareFunc(t, router.autoRedirect[0], middleware0)
|
||||
compareFunc(t, router.allAutoRedirect[0], middleware2)
|
||||
compareFunc(t, router.allAutoRedirect[1], middleware0)
|
||||
|
||||
router.Use(middleware1)
|
||||
assert.Len(t, router.allAutoRedirect, 3)
|
||||
assert.Len(t, router.Handlers, 2)
|
||||
assert.Len(t, router.autoRedirect, 1)
|
||||
|
||||
compareFunc(t, router.Handlers[0], middleware2)
|
||||
compareFunc(t, router.Handlers[1], middleware1)
|
||||
compareFunc(t, router.autoRedirect[0], middleware0)
|
||||
compareFunc(t, router.allAutoRedirect[0], middleware2)
|
||||
compareFunc(t, router.allAutoRedirect[1], middleware1)
|
||||
compareFunc(t, router.allAutoRedirect[2], middleware0)
|
||||
}
|
||||
|
||||
func compareFunc(t *testing.T, a, b any) {
|
||||
sf1 := reflect.ValueOf(a)
|
||||
sf2 := reflect.ValueOf(b)
|
||||
|
||||
@ -30,7 +30,7 @@ func rawStrToBytes(s string) []byte {
|
||||
|
||||
func TestBytesToString(t *testing.T) {
|
||||
data := make([]byte, 1024)
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
_, err := cRand.Read(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -79,7 +79,7 @@ func RandStringBytesMaskImprSrcSB(n int) string {
|
||||
}
|
||||
|
||||
func TestStringToBytes(t *testing.T) {
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
s := RandStringBytesMaskImprSrcSB(64)
|
||||
if !bytes.Equal(rawStrToBytes(s), StringToBytes(s)) {
|
||||
t.Fatal("don't match")
|
||||
|
||||
@ -273,6 +273,27 @@ func TestRouteRedirectFixedPath(t *testing.T) {
|
||||
assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
|
||||
}
|
||||
|
||||
func TestRouteRedirectWithHandler(t *testing.T) {
|
||||
router := New()
|
||||
router.RedirectTrailingSlash = true
|
||||
router.GET("/path", func(c *Context) {})
|
||||
passed := []bool{false, false}
|
||||
router.Use(func(c *Context) {
|
||||
passed[0] = true
|
||||
c.Next()
|
||||
})
|
||||
router.AutoRedirect(func(c *Context) {
|
||||
passed[1] = true
|
||||
c.Next()
|
||||
})
|
||||
|
||||
w := performRequest(router, http.MethodGet, "/path/")
|
||||
assert.Equal(t, "/path", w.Header().Get("Location"))
|
||||
assert.Equal(t, http.StatusMovedPermanently, w.Code)
|
||||
assert.True(t, passed[0])
|
||||
assert.True(t, passed[1])
|
||||
}
|
||||
|
||||
// TestContextParamsGet tests that a parameter can be parsed from the URL.
|
||||
func TestRouteParamsByName(t *testing.T) {
|
||||
name := ""
|
||||
|
||||
7
tree.go
7
tree.go
@ -671,12 +671,7 @@ walk: // Outer loop for walking the tree
|
||||
func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) ([]byte, bool) {
|
||||
const stackBufSize = 128
|
||||
|
||||
// Use a static sized buffer on the stack in the common case.
|
||||
// If the path is too long, allocate a buffer on the heap instead.
|
||||
buf := make([]byte, 0, stackBufSize)
|
||||
if length := len(path) + 1; length > stackBufSize {
|
||||
buf = make([]byte, 0, length)
|
||||
}
|
||||
buf := make([]byte, 0, max(stackBufSize, len(path)+1))
|
||||
|
||||
ciPath := n.findCaseInsensitivePathRec(
|
||||
path,
|
||||
|
||||
2
utils.go
2
utils.go
@ -162,7 +162,7 @@ func resolveAddress(addr []string) string {
|
||||
|
||||
// https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters
|
||||
func isASCII(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
for i := range len(s) {
|
||||
if s[i] > unicode.MaxASCII {
|
||||
return false
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user