mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-16 15:09:11 +08:00
Merge branch 'master' of https://github.com/gin-gonic/gin into gin-gonic-master
This commit is contained in:
commit
e3f85d8161
@ -1793,8 +1793,8 @@ func main() {
|
|||||||
// Initializing the server in a goroutine so that
|
// Initializing the server in a goroutine so that
|
||||||
// it won't block the graceful shutdown handling below
|
// it won't block the graceful shutdown handling below
|
||||||
go func() {
|
go func() {
|
||||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
|
||||||
log.Fatalf("listen: %s\n", err)
|
log.Printf("listen: %s\n", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -1812,6 +1812,7 @@ func main() {
|
|||||||
// the request it is currently handling
|
// the request it is currently handling
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if err := srv.Shutdown(ctx); err != nil {
|
if err := srv.Shutdown(ctx); err != nil {
|
||||||
log.Fatal("Server forced to shutdown:", err)
|
log.Fatal("Server forced to shutdown:", err)
|
||||||
}
|
}
|
||||||
|
|||||||
3
auth.go
3
auth.go
@ -5,6 +5,7 @@
|
|||||||
package gin
|
package gin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -30,7 +31,7 @@ func (a authPairs) searchCredential(authValue string) (string, bool) {
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
for _, pair := range a {
|
for _, pair := range a {
|
||||||
if pair.value == authValue {
|
if subtle.ConstantTimeCompare([]byte(pair.value), []byte(authValue)) == 1 {
|
||||||
return pair.user, true
|
return pair.user, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
// +build !nomsgpack
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
@ -51,7 +52,8 @@ type BindingUri interface {
|
|||||||
// https://github.com/go-playground/validator/tree/v8.18.2.
|
// https://github.com/go-playground/validator/tree/v8.18.2.
|
||||||
type StructValidator interface {
|
type StructValidator interface {
|
||||||
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
||||||
// If the received type is not a struct, any validation should be skipped and nil must be returned.
|
// If the received type is a slice|array, the validation should be performed travel on every element.
|
||||||
|
// If the received type is not a struct or slice|array, any validation should be skipped and nil must be returned.
|
||||||
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
||||||
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
||||||
// Otherwise nil must be returned.
|
// Otherwise nil must be returned.
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
// +build !nomsgpack
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build nomsgpack
|
||||||
// +build nomsgpack
|
// +build nomsgpack
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|||||||
@ -35,7 +35,7 @@ type QueryTest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FooStruct struct {
|
type FooStruct struct {
|
||||||
Foo string `msgpack:"foo" json:"foo" form:"foo" xml:"foo" binding:"required"`
|
Foo string `msgpack:"foo" json:"foo" form:"foo" xml:"foo" binding:"required,max=32"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FooBarStruct struct {
|
type FooBarStruct struct {
|
||||||
@ -181,6 +181,20 @@ func TestBindingJSON(t *testing.T) {
|
|||||||
`{"foo": "bar"}`, `{"bar": "foo"}`)
|
`{"foo": "bar"}`, `{"bar": "foo"}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBindingJSONSlice(t *testing.T) {
|
||||||
|
EnableDecoderDisallowUnknownFields = true
|
||||||
|
defer func() {
|
||||||
|
EnableDecoderDisallowUnknownFields = false
|
||||||
|
}()
|
||||||
|
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[]`, ``)
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{"foo": "123"}]`, `[{}]`)
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{"foo": "123"}]`, `[{"foo": ""}]`)
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{"foo": "123"}]`, `[{"foo": 123}]`)
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{"foo": "123"}]`, `[{"bar": 123}]`)
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{"foo": "123"}]`, `[{"foo": "123456789012345678901234567890123"}]`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestBindingJSONUseNumber(t *testing.T) {
|
func TestBindingJSONUseNumber(t *testing.T) {
|
||||||
testBodyBindingUseNumber(t,
|
testBodyBindingUseNumber(t,
|
||||||
JSON, "json",
|
JSON, "json",
|
||||||
@ -1181,6 +1195,20 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
|
|||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testBodyBindingSlice(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
|
||||||
|
assert.Equal(t, name, b.Name())
|
||||||
|
|
||||||
|
var obj1 []FooStruct
|
||||||
|
req := requestWithBody("POST", path, body)
|
||||||
|
err := b.Bind(req, &obj1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
var obj2 []FooStruct
|
||||||
|
req = requestWithBody("POST", badPath, badBody)
|
||||||
|
err = JSON.Bind(req, &obj2)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) {
|
func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) {
|
||||||
obj := make(map[string]string)
|
obj := make(map[string]string)
|
||||||
req := requestWithBody("POST", path, body)
|
req := requestWithBody("POST", path, body)
|
||||||
|
|||||||
@ -5,7 +5,9 @@
|
|||||||
package binding
|
package binding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
@ -16,24 +18,56 @@ type defaultValidator struct {
|
|||||||
validate *validator.Validate
|
validate *validator.Validate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sliceValidateError []error
|
||||||
|
|
||||||
|
func (err sliceValidateError) Error() string {
|
||||||
|
var errMsgs []string
|
||||||
|
for i, e := range err {
|
||||||
|
if e == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
errMsgs = append(errMsgs, fmt.Sprintf("[%d]: %s", i, e.Error()))
|
||||||
|
}
|
||||||
|
return strings.Join(errMsgs, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
var _ StructValidator = &defaultValidator{}
|
var _ StructValidator = &defaultValidator{}
|
||||||
|
|
||||||
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
|
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
|
||||||
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
||||||
value := reflect.ValueOf(obj)
|
if obj == nil {
|
||||||
valueType := value.Kind()
|
|
||||||
if valueType == reflect.Ptr {
|
|
||||||
valueType = value.Elem().Kind()
|
|
||||||
}
|
|
||||||
if valueType == reflect.Struct {
|
|
||||||
v.lazyinit()
|
|
||||||
if err := v.validate.Struct(obj); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value := reflect.ValueOf(obj)
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
return v.ValidateStruct(value.Elem().Interface())
|
||||||
|
case reflect.Struct:
|
||||||
|
return v.validateStruct(obj)
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
count := value.Len()
|
||||||
|
validateRet := make(sliceValidateError, 0)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
if err := v.ValidateStruct(value.Index(i).Interface()); err != nil {
|
||||||
|
validateRet = append(validateRet, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(validateRet) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return validateRet
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateStruct receives struct type
|
||||||
|
func (v *defaultValidator) validateStruct(obj interface{}) error {
|
||||||
|
v.lazyinit()
|
||||||
|
return v.validate.Struct(obj)
|
||||||
|
}
|
||||||
|
|
||||||
// Engine returns the underlying validator engine which powers the default
|
// Engine returns the underlying validator engine which powers the default
|
||||||
// Validator instance. This is useful if you want to register custom validations
|
// Validator instance. This is useful if you want to register custom validations
|
||||||
// or struct level validations. See validator GoDoc for more info -
|
// or struct level validations. See validator GoDoc for more info -
|
||||||
|
|||||||
68
binding/default_validator_test.go
Normal file
68
binding/default_validator_test.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2020 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSliceValidateError(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
err sliceValidateError
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"has nil elements", sliceValidateError{errors.New("test error"), nil}, "[0]: test error"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := tt.err.Error(); got != tt.want {
|
||||||
|
t.Errorf("sliceValidateError.Error() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultValidator(t *testing.T) {
|
||||||
|
type exampleStruct struct {
|
||||||
|
A string `binding:"max=8"`
|
||||||
|
B int `binding:"gt=0"`
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
v *defaultValidator
|
||||||
|
obj interface{}
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"validate nil obj", &defaultValidator{}, nil, false},
|
||||||
|
{"validate int obj", &defaultValidator{}, 3, false},
|
||||||
|
{"validate struct failed-1", &defaultValidator{}, exampleStruct{A: "123456789", B: 1}, true},
|
||||||
|
{"validate struct failed-2", &defaultValidator{}, exampleStruct{A: "12345678", B: 0}, true},
|
||||||
|
{"validate struct passed", &defaultValidator{}, exampleStruct{A: "12345678", B: 1}, false},
|
||||||
|
{"validate *struct failed-1", &defaultValidator{}, &exampleStruct{A: "123456789", B: 1}, true},
|
||||||
|
{"validate *struct failed-2", &defaultValidator{}, &exampleStruct{A: "12345678", B: 0}, true},
|
||||||
|
{"validate *struct passed", &defaultValidator{}, &exampleStruct{A: "12345678", B: 1}, false},
|
||||||
|
{"validate []struct failed-1", &defaultValidator{}, []exampleStruct{{A: "123456789", B: 1}}, true},
|
||||||
|
{"validate []struct failed-2", &defaultValidator{}, []exampleStruct{{A: "12345678", B: 0}}, true},
|
||||||
|
{"validate []struct passed", &defaultValidator{}, []exampleStruct{{A: "12345678", B: 1}}, false},
|
||||||
|
{"validate []*struct failed-1", &defaultValidator{}, []*exampleStruct{{A: "123456789", B: 1}}, true},
|
||||||
|
{"validate []*struct failed-2", &defaultValidator{}, []*exampleStruct{{A: "12345678", B: 0}}, true},
|
||||||
|
{"validate []*struct passed", &defaultValidator{}, []*exampleStruct{{A: "12345678", B: 1}}, false},
|
||||||
|
{"validate *[]struct failed-1", &defaultValidator{}, &[]exampleStruct{{A: "123456789", B: 1}}, true},
|
||||||
|
{"validate *[]struct failed-2", &defaultValidator{}, &[]exampleStruct{{A: "12345678", B: 0}}, true},
|
||||||
|
{"validate *[]struct passed", &defaultValidator{}, &[]exampleStruct{{A: "12345678", B: 1}}, false},
|
||||||
|
{"validate *[]*struct failed-1", &defaultValidator{}, &[]*exampleStruct{{A: "123456789", B: 1}}, true},
|
||||||
|
{"validate *[]*struct failed-2", &defaultValidator{}, &[]*exampleStruct{{A: "12345678", B: 0}}, true},
|
||||||
|
{"validate *[]*struct passed", &defaultValidator{}, &[]*exampleStruct{{A: "12345678", B: 1}}, false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := tt.v.ValidateStruct(tt.obj); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("defaultValidator.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
// +build !nomsgpack
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
// +build !nomsgpack
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
// +build appengine
|
|
||||||
|
|
||||||
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build appengine
|
||||||
|
// +build appengine
|
||||||
|
|
||||||
package gin
|
package gin
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
4
debug.go
4
debug.go
@ -12,7 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ginSupportMinGoVer = 10
|
const ginSupportMinGoVer = 12
|
||||||
|
|
||||||
// IsDebugging returns true if the framework is running in debug mode.
|
// IsDebugging returns true if the framework is running in debug mode.
|
||||||
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
||||||
@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) {
|
|||||||
|
|
||||||
func debugPrintWARNINGDefault() {
|
func debugPrintWARNINGDefault() {
|
||||||
if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
|
if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
|
||||||
debugPrint(`[WARNING] Now Gin requires Go 1.11 or later and Go 1.12 will be required soon.
|
debugPrint(`[WARNING] Now Gin requires Go 1.12+.
|
||||||
|
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) {
|
|||||||
})
|
})
|
||||||
m, e := getMinVer(runtime.Version())
|
m, e := getMinVer(runtime.Version())
|
||||||
if e == nil && m <= ginSupportMinGoVer {
|
if e == nil && m <= ginSupportMinGoVer {
|
||||||
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.11 or later and Go 1.12 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.12+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||||
} else {
|
} else {
|
||||||
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
//go:build go1.13
|
||||||
// +build go1.13
|
// +build go1.13
|
||||||
|
|
||||||
package gin
|
package gin
|
||||||
|
|||||||
@ -5,16 +5,17 @@
|
|||||||
package bytesconv
|
package bytesconv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StringToBytes converts string to byte slice without a memory allocation.
|
// StringToBytes converts string to byte slice without a memory allocation.
|
||||||
func StringToBytes(s string) (b []byte) {
|
func StringToBytes(s string) []byte {
|
||||||
sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
|
return *(*[]byte)(unsafe.Pointer(
|
||||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
&struct {
|
||||||
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
|
string
|
||||||
return b
|
Cap int
|
||||||
|
}{s, len(s)},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesToString converts byte slice to string without a memory allocation.
|
// BytesToString converts byte slice to string without a memory allocation.
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !jsoniter
|
||||||
// +build !jsoniter
|
// +build !jsoniter
|
||||||
|
|
||||||
package json
|
package json
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build jsoniter
|
||||||
// +build jsoniter
|
// +build jsoniter
|
||||||
|
|
||||||
package json
|
package json
|
||||||
|
|||||||
2
mode.go
2
mode.go
@ -63,7 +63,7 @@ func SetMode(value string) {
|
|||||||
case TestMode:
|
case TestMode:
|
||||||
ginMode = testCode
|
ginMode = testCode
|
||||||
default:
|
default:
|
||||||
panic("gin mode unknown: " + value)
|
panic("gin mode unknown: " + value + " (available mode: debug release test)")
|
||||||
}
|
}
|
||||||
|
|
||||||
modeName = value
|
modeName = value
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
// +build !nomsgpack
|
||||||
|
|
||||||
package render
|
package render
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
// +build !nomsgpack
|
||||||
|
|
||||||
package render
|
package render
|
||||||
|
|||||||
@ -238,7 +238,6 @@ func TestRouteParamsByName(t *testing.T) {
|
|||||||
|
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equal(t, name, c.Param("name"))
|
assert.Equal(t, name, c.Param("name"))
|
||||||
assert.Equal(t, name, c.Param("name"))
|
|
||||||
assert.Equal(t, lastName, c.Param("last_name"))
|
assert.Equal(t, lastName, c.Param("last_name"))
|
||||||
|
|
||||||
assert.Empty(t, c.Param("wtf"))
|
assert.Empty(t, c.Param("wtf"))
|
||||||
@ -272,7 +271,6 @@ func TestRouteParamsByNameWithExtraSlash(t *testing.T) {
|
|||||||
|
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equal(t, name, c.Param("name"))
|
assert.Equal(t, name, c.Param("name"))
|
||||||
assert.Equal(t, name, c.Param("name"))
|
|
||||||
assert.Equal(t, lastName, c.Param("last_name"))
|
assert.Equal(t, lastName, c.Param("last_name"))
|
||||||
|
|
||||||
assert.Empty(t, c.Param("wtf"))
|
assert.Empty(t, c.Param("wtf"))
|
||||||
|
|||||||
53
tree.go
53
tree.go
@ -119,7 +119,6 @@ func (n *node) incrementChildPrio(pos int) int {
|
|||||||
for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- {
|
for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- {
|
||||||
// Swap node positions
|
// Swap node positions
|
||||||
cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1]
|
cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build new index char string
|
// Build new index char string
|
||||||
@ -559,8 +558,8 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) ([]by
|
|||||||
// Use a static sized buffer on the stack in the common case.
|
// 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.
|
// If the path is too long, allocate a buffer on the heap instead.
|
||||||
buf := make([]byte, 0, stackBufSize)
|
buf := make([]byte, 0, stackBufSize)
|
||||||
if l := len(path) + 1; l > stackBufSize {
|
if length := len(path) + 1; length > stackBufSize {
|
||||||
buf = make([]byte, 0, l)
|
buf = make([]byte, 0, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
ciPath := n.findCaseInsensitivePathRec(
|
ciPath := n.findCaseInsensitivePathRec(
|
||||||
@ -600,7 +599,30 @@ walk: // Outer loop for walking the tree
|
|||||||
path = path[npLen:]
|
path = path[npLen:]
|
||||||
ciPath = append(ciPath, n.path...)
|
ciPath = append(ciPath, n.path...)
|
||||||
|
|
||||||
if len(path) > 0 {
|
if len(path) == 0 {
|
||||||
|
// We should have reached the node containing the handle.
|
||||||
|
// Check if this node has a handle registered.
|
||||||
|
if n.handlers != nil {
|
||||||
|
return ciPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// No handle found.
|
||||||
|
// Try to fix the path by adding a trailing slash
|
||||||
|
if fixTrailingSlash {
|
||||||
|
for i, c := range []byte(n.indices) {
|
||||||
|
if c == '/' {
|
||||||
|
n = n.children[i]
|
||||||
|
if (len(n.path) == 1 && n.handlers != nil) ||
|
||||||
|
(n.nType == catchAll && n.children[0].handlers != nil) {
|
||||||
|
return append(ciPath, '/')
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// If this node does not have a wildcard (param or catchAll) child,
|
// If this node does not have a wildcard (param or catchAll) child,
|
||||||
// we can just look up the next child node and continue to walk down
|
// we can just look up the next child node and continue to walk down
|
||||||
// the tree
|
// the tree
|
||||||
@ -735,29 +757,6 @@ walk: // Outer loop for walking the tree
|
|||||||
default:
|
default:
|
||||||
panic("invalid node type")
|
panic("invalid node type")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// We should have reached the node containing the handle.
|
|
||||||
// Check if this node has a handle registered.
|
|
||||||
if n.handlers != nil {
|
|
||||||
return ciPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// No handle found.
|
|
||||||
// Try to fix the path by adding a trailing slash
|
|
||||||
if fixTrailingSlash {
|
|
||||||
for i, c := range []byte(n.indices) {
|
|
||||||
if c == '/' {
|
|
||||||
n = n.children[i]
|
|
||||||
if (len(n.path) == 1 && n.handlers != nil) ||
|
|
||||||
(n.nType == catchAll && n.children[0].handlers != nil) {
|
|
||||||
return append(ciPath, '/')
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing found.
|
// Nothing found.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user