mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-17 14:12:16 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
263d2b91b5
1
.github/workflows/gin.yml
vendored
1
.github/workflows/gin.yml
vendored
@ -24,6 +24,7 @@ jobs:
|
|||||||
version: v1.42.1
|
version: v1.42.1
|
||||||
args: --verbose
|
args: --verbose
|
||||||
test:
|
test:
|
||||||
|
needs: lint
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest]
|
os: [ubuntu-latest, macos-latest]
|
||||||
|
@ -2,21 +2,38 @@ run:
|
|||||||
timeout: 5m
|
timeout: 5m
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
- gofmt
|
- asciicheck
|
||||||
- misspell
|
- depguard
|
||||||
- revive
|
- dogsled
|
||||||
|
- durationcheck
|
||||||
|
- errcheck
|
||||||
|
- errorlint
|
||||||
|
- exportloopref
|
||||||
|
- gci
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
- gosec
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- nilerr
|
||||||
|
- nolintlint
|
||||||
|
- revive
|
||||||
|
- wastedassign
|
||||||
issues:
|
issues:
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
- linters:
|
- linters:
|
||||||
- structcheck
|
- structcheck
|
||||||
- unused
|
- unused
|
||||||
text: "`data` is unused"
|
text: "`data` is unused"
|
||||||
- linters:
|
- linters:
|
||||||
- staticcheck
|
- staticcheck
|
||||||
text: "SA1019:"
|
text: "SA1019:"
|
||||||
- linters:
|
- linters:
|
||||||
- revive
|
- revive
|
||||||
text: "var-naming:"
|
text: "var-naming:"
|
||||||
- linters:
|
- linters:
|
||||||
- revive
|
- revive
|
||||||
text: "exported:"
|
text: "exported:"
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- gosec # security is not make sense in tests
|
||||||
|
14
README.md
14
README.md
@ -1827,7 +1827,7 @@ func main() {
|
|||||||
quit := make(chan os.Signal)
|
quit := make(chan os.Signal)
|
||||||
// kill (no param) default send syscall.SIGTERM
|
// kill (no param) default send syscall.SIGTERM
|
||||||
// kill -2 is syscall.SIGINT
|
// kill -2 is syscall.SIGINT
|
||||||
// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
|
// kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it
|
||||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
<-quit
|
<-quit
|
||||||
log.Println("Shutting down server...")
|
log.Println("Shutting down server...")
|
||||||
@ -2202,11 +2202,17 @@ Gin lets you specify which headers to hold the real client IP (if any),
|
|||||||
as well as specifying which proxies (or direct clients) you trust to
|
as well as specifying which proxies (or direct clients) you trust to
|
||||||
specify one of these headers.
|
specify one of these headers.
|
||||||
|
|
||||||
The `TrustedProxies` slice on your `gin.Engine` specifes network addresses or
|
Use function `SetTrustedProxies()` on your `gin.Engine` to specify network addresses
|
||||||
network CIDRs from where clients which their request headers related to client
|
or network CIDRs from where clients which their request headers related to client
|
||||||
IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
|
IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
|
||||||
IPv6 CIDRs.
|
IPv6 CIDRs.
|
||||||
|
|
||||||
|
**Attention:** Gin trust all proxies by default if you don't specify a trusted
|
||||||
|
proxy using the function above, **this is NOT safe**. At the same time, if you don't
|
||||||
|
use any proxy, you can disable this feature by using `Engine.SetTrustedProxies(nil)`,
|
||||||
|
then `Context.ClientIP()` will return the remote address directly to avoid some
|
||||||
|
unnecessary computation.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -2217,7 +2223,7 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
router.TrustedProxies = []string{"192.168.1.2"}
|
router.SetTrustedProxies([]string{"192.168.1.2"})
|
||||||
|
|
||||||
router.GET("/", func(c *gin.Context) {
|
router.GET("/", func(c *gin.Context) {
|
||||||
// If the client is 192.168.1.2, use the X-Forwarded-For
|
// If the client is 192.168.1.2, use the X-Forwarded-For
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package binding
|
package binding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ func (formBinding) Bind(req *http.Request, obj interface{}) error {
|
|||||||
if err := req.ParseForm(); err != nil {
|
if err := req.ParseForm(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := req.ParseMultipartForm(defaultMemory); err != nil && err != http.ErrNotMultipart {
|
if err := req.ParseMultipartForm(defaultMemory); err != nil && !errors.Is(err, http.ErrNotMultipart) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := mapForm(obj, req.Form); err != nil {
|
if err := mapForm(obj, req.Form); err != nil {
|
||||||
|
@ -61,7 +61,7 @@ func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
|
|||||||
|
|
||||||
// setter tries to set value on a walking by fields of a struct
|
// setter tries to set value on a walking by fields of a struct
|
||||||
type setter interface {
|
type setter interface {
|
||||||
TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error)
|
TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSet bool, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type formSource map[string][]string
|
type formSource map[string][]string
|
||||||
@ -69,7 +69,7 @@ type formSource map[string][]string
|
|||||||
var _ setter = formSource(nil)
|
var _ setter = formSource(nil)
|
||||||
|
|
||||||
// TrySet tries to set a value by request's form source (like map[string][]string)
|
// TrySet tries to set a value by request's form source (like map[string][]string)
|
||||||
func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
|
func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSet bool, err error) {
|
||||||
return setByForm(value, field, form, tagValue, opt)
|
return setByForm(value, field, form, tagValue, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,14 +92,14 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
|
|||||||
isNew = true
|
isNew = true
|
||||||
vPtr = reflect.New(value.Type().Elem())
|
vPtr = reflect.New(value.Type().Elem())
|
||||||
}
|
}
|
||||||
isSetted, err := mapping(vPtr.Elem(), field, setter, tag)
|
isSet, err := mapping(vPtr.Elem(), field, setter, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if isNew && isSetted {
|
if isNew && isSet {
|
||||||
value.Set(vPtr)
|
value.Set(vPtr)
|
||||||
}
|
}
|
||||||
return isSetted, nil
|
return isSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if vKind != reflect.Struct || !field.Anonymous {
|
if vKind != reflect.Struct || !field.Anonymous {
|
||||||
@ -115,7 +115,7 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
|
|||||||
if vKind == reflect.Struct {
|
if vKind == reflect.Struct {
|
||||||
tValue := value.Type()
|
tValue := value.Type()
|
||||||
|
|
||||||
var isSetted bool
|
var isSet bool
|
||||||
for i := 0; i < value.NumField(); i++ {
|
for i := 0; i < value.NumField(); i++ {
|
||||||
sf := tValue.Field(i)
|
sf := tValue.Field(i)
|
||||||
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
||||||
@ -125,9 +125,9 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
isSetted = isSetted || ok
|
isSet = isSet || ok
|
||||||
}
|
}
|
||||||
return isSetted, nil
|
return isSet, nil
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter
|
|||||||
return setter.TrySet(value, field, tagValue, setOpt)
|
return setter.TrySet(value, field, tagValue, setOpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) {
|
func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSet bool, err error) {
|
||||||
vs, ok := form[tagValue]
|
vs, ok := form[tagValue]
|
||||||
if !ok && !opt.isDefaultExists {
|
if !ok && !opt.isDefaultExists {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -32,7 +32,7 @@ func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField
|
|||||||
return setByForm(value, field, r.MultipartForm.Value, key, opt)
|
return setByForm(value, field, r.MultipartForm.Value, key, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
|
func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSet bool, err error) {
|
||||||
switch value.Kind() {
|
switch value.Kind() {
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
switch value.Interface().(type) {
|
switch value.Interface().(type) {
|
||||||
@ -48,9 +48,9 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file
|
|||||||
}
|
}
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
slice := reflect.MakeSlice(value.Type(), len(files), len(files))
|
slice := reflect.MakeSlice(value.Type(), len(files), len(files))
|
||||||
isSetted, err = setArrayOfMultipartFormFiles(slice, field, files)
|
isSet, err = setArrayOfMultipartFormFiles(slice, field, files)
|
||||||
if err != nil || !isSetted {
|
if err != nil || !isSet {
|
||||||
return isSetted, err
|
return isSet, err
|
||||||
}
|
}
|
||||||
value.Set(slice)
|
value.Set(slice)
|
||||||
return true, nil
|
return true, nil
|
||||||
@ -60,14 +60,14 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file
|
|||||||
return false, ErrMultiFileHeader
|
return false, ErrMultiFileHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
|
func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSet bool, err error) {
|
||||||
if value.Len() != len(files) {
|
if value.Len() != len(files) {
|
||||||
return false, ErrMultiFileHeaderLenInvalid
|
return false, ErrMultiFileHeaderLenInvalid
|
||||||
}
|
}
|
||||||
for i := range files {
|
for i := range files {
|
||||||
setted, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1])
|
set, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1])
|
||||||
if err != nil || !setted {
|
if err != nil || !set {
|
||||||
return setted, err
|
return set, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
11
context.go
11
context.go
@ -220,7 +220,8 @@ func (c *Context) Error(err error) *Error {
|
|||||||
panic("err is nil")
|
panic("err is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedError, ok := err.(*Error)
|
var parsedError *Error
|
||||||
|
ok := errors.As(err, &parsedError)
|
||||||
if !ok {
|
if !ok {
|
||||||
parsedError = &Error{
|
parsedError = &Error{
|
||||||
Err: err,
|
Err: err,
|
||||||
@ -515,7 +516,7 @@ func (c *Context) initFormCache() {
|
|||||||
c.formCache = make(url.Values)
|
c.formCache = make(url.Values)
|
||||||
req := c.Request
|
req := c.Request
|
||||||
if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
|
if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
|
||||||
if err != http.ErrNotMultipart {
|
if !errors.Is(err, http.ErrNotMultipart) {
|
||||||
debugPrint("error on parse multipart form array: %v", err)
|
debugPrint("error on parse multipart form array: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -734,8 +735,8 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e
|
|||||||
|
|
||||||
// ClientIP implements a best effort algorithm to return the real client IP.
|
// ClientIP implements a best effort algorithm to return the real client IP.
|
||||||
// It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
|
// It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
|
||||||
// If it's it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
|
// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
|
||||||
// If the headers are nots syntactically valid OR the remote IP does not correspong to a trusted proxy,
|
// If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy,
|
||||||
// the remote IP (coming form Request.RemoteAddr) is returned.
|
// the remote IP (coming form Request.RemoteAddr) is returned.
|
||||||
func (c *Context) ClientIP() string {
|
func (c *Context) ClientIP() string {
|
||||||
// Check if we're running on a trusted platform
|
// Check if we're running on a trusted platform
|
||||||
@ -777,7 +778,7 @@ func (c *Context) ClientIP() string {
|
|||||||
// RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port).
|
// RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port).
|
||||||
// It also checks if the remoteIP is a trusted proxy or not.
|
// It also checks if the remoteIP is a trusted proxy or not.
|
||||||
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
|
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
|
||||||
// defined in Engine.TrustedProxies
|
// defined by Engine.SetTrustedProxies()
|
||||||
func (c *Context) RemoteIP() (net.IP, bool) {
|
func (c *Context) RemoteIP() (net.IP, bool) {
|
||||||
ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
|
ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -23,10 +23,9 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-contrib/sse"
|
"github.com/gin-contrib/sse"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
testdata "github.com/gin-gonic/gin/testdata/protoexample"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
testdata "github.com/gin-gonic/gin/testdata/protoexample"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ context.Context = &Context{}
|
var _ context.Context = &Context{}
|
||||||
@ -1410,6 +1409,10 @@ func TestContextClientIP(t *testing.T) {
|
|||||||
c.engine.RemoteIPHeaders = []string{"X-Forwarded-For"}
|
c.engine.RemoteIPHeaders = []string{"X-Forwarded-For"}
|
||||||
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
|
||||||
|
// Disabled TrustedProxies feature
|
||||||
|
_ = c.engine.SetTrustedProxies(nil)
|
||||||
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
|
||||||
// Last proxy is trusted, but the RemoteAddr is not
|
// Last proxy is trusted, but the RemoteAddr is not
|
||||||
_ = c.engine.SetTrustedProxies([]string{"30.30.30.30"})
|
_ = c.engine.SetTrustedProxies([]string{"30.30.30.30"})
|
||||||
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
@ -113,7 +113,7 @@ func (e TestErr) Error() string { return string(e) }
|
|||||||
// TestErrorUnwrap tests the behavior of gin.Error with "errors.Is()" and "errors.As()".
|
// TestErrorUnwrap tests the behavior of gin.Error with "errors.Is()" and "errors.As()".
|
||||||
// "errors.Is()" and "errors.As()" have been added to the standard library in go 1.13.
|
// "errors.Is()" and "errors.As()" have been added to the standard library in go 1.13.
|
||||||
func TestErrorUnwrap(t *testing.T) {
|
func TestErrorUnwrap(t *testing.T) {
|
||||||
innerErr := TestErr("somme error")
|
innerErr := TestErr("some error")
|
||||||
|
|
||||||
// 2 layers of wrapping : use 'fmt.Errorf("%w")' to wrap a gin.Error{}, which itself wraps innerErr
|
// 2 layers of wrapping : use 'fmt.Errorf("%w")' to wrap a gin.Error{}, which itself wraps innerErr
|
||||||
err := fmt.Errorf("wrapped: %w", &Error{
|
err := fmt.Errorf("wrapped: %w", &Error{
|
||||||
|
71
gin.go
71
gin.go
@ -11,6 +11,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -27,6 +28,8 @@ var (
|
|||||||
|
|
||||||
var defaultPlatform string
|
var defaultPlatform string
|
||||||
|
|
||||||
|
var defaultTrustedCIDRs = []*net.IPNet{{IP: net.IP{0x0, 0x0, 0x0, 0x0}, Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}}} // 0.0.0.0/0
|
||||||
|
|
||||||
// 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)
|
||||||
|
|
||||||
@ -119,15 +122,9 @@ type Engine struct {
|
|||||||
// List of headers used to obtain the client IP when
|
// List of headers used to obtain the client IP when
|
||||||
// `(*gin.Engine).ForwardedByClientIP` is `true` and
|
// `(*gin.Engine).ForwardedByClientIP` is `true` and
|
||||||
// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
|
// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
|
||||||
// network origins of `(*gin.Engine).TrustedProxies`.
|
// network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
|
||||||
RemoteIPHeaders []string
|
RemoteIPHeaders []string
|
||||||
|
|
||||||
// List of network origins (IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
|
|
||||||
// IPv6 CIDRs) from which to trust request's headers that contain
|
|
||||||
// alternative client IP when `(*gin.Engine).ForwardedByClientIP` is
|
|
||||||
// `true`.
|
|
||||||
TrustedProxies []string
|
|
||||||
|
|
||||||
// If set to a constant of value gin.Platform*, trusts the headers set by
|
// If set to a constant of value gin.Platform*, trusts the headers set by
|
||||||
// that platform, for example to determine the client IP
|
// that platform, for example to determine the client IP
|
||||||
TrustedPlatform string
|
TrustedPlatform string
|
||||||
@ -147,6 +144,7 @@ type Engine struct {
|
|||||||
pool sync.Pool
|
pool sync.Pool
|
||||||
trees methodTrees
|
trees methodTrees
|
||||||
maxParams uint16
|
maxParams uint16
|
||||||
|
trustedProxies []string
|
||||||
trustedCIDRs []*net.IPNet
|
trustedCIDRs []*net.IPNet
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +172,6 @@ func New() *Engine {
|
|||||||
HandleMethodNotAllowed: false,
|
HandleMethodNotAllowed: false,
|
||||||
ForwardedByClientIP: true,
|
ForwardedByClientIP: true,
|
||||||
RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
|
RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
|
||||||
TrustedProxies: []string{"0.0.0.0/0"},
|
|
||||||
TrustedPlatform: defaultPlatform,
|
TrustedPlatform: defaultPlatform,
|
||||||
UseRawPath: false,
|
UseRawPath: false,
|
||||||
RemoveExtraSlash: false,
|
RemoveExtraSlash: false,
|
||||||
@ -183,6 +180,8 @@ func New() *Engine {
|
|||||||
trees: make(methodTrees, 0, 9),
|
trees: make(methodTrees, 0, 9),
|
||||||
delims: render.Delims{Left: "{{", Right: "}}"},
|
delims: render.Delims{Left: "{{", Right: "}}"},
|
||||||
secureJSONPrefix: "while(1);",
|
secureJSONPrefix: "while(1);",
|
||||||
|
trustedProxies: []string{"0.0.0.0/0"},
|
||||||
|
trustedCIDRs: defaultTrustedCIDRs,
|
||||||
}
|
}
|
||||||
engine.RouterGroup.engine = engine
|
engine.RouterGroup.engine = engine
|
||||||
engine.pool.New = func() interface{} {
|
engine.pool.New = func() interface{} {
|
||||||
@ -264,7 +263,7 @@ func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
|
|||||||
engine.rebuild404Handlers()
|
engine.rebuild404Handlers()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoMethod sets the handlers called when... TODO.
|
// NoMethod sets the handlers called when Engine.HandleMethodNotAllowed = true.
|
||||||
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
|
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
|
||||||
engine.noMethod = handlers
|
engine.noMethod = handlers
|
||||||
engine.rebuild405Handlers()
|
engine.rebuild405Handlers()
|
||||||
@ -341,9 +340,9 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
|
|||||||
func (engine *Engine) Run(addr ...string) (err error) {
|
func (engine *Engine) Run(addr ...string) (err error) {
|
||||||
defer func() { debugPrintError(err) }()
|
defer func() { debugPrintError(err) }()
|
||||||
|
|
||||||
err = engine.parseTrustedProxies()
|
if engine.isUnsafeTrustedProxies() {
|
||||||
if err != nil {
|
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
||||||
return err
|
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
|
||||||
}
|
}
|
||||||
|
|
||||||
address := resolveAddress(addr)
|
address := resolveAddress(addr)
|
||||||
@ -353,12 +352,12 @@ func (engine *Engine) Run(addr ...string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
|
func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
|
||||||
if engine.TrustedProxies == nil {
|
if engine.trustedProxies == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cidr := make([]*net.IPNet, 0, len(engine.TrustedProxies))
|
cidr := make([]*net.IPNet, 0, len(engine.trustedProxies))
|
||||||
for _, trustedProxy := range engine.TrustedProxies {
|
for _, trustedProxy := range engine.trustedProxies {
|
||||||
if !strings.Contains(trustedProxy, "/") {
|
if !strings.Contains(trustedProxy, "/") {
|
||||||
ip := parseIP(trustedProxy)
|
ip := parseIP(trustedProxy)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
@ -381,13 +380,25 @@ func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
|
|||||||
return cidr, nil
|
return cidr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTrustedProxies set Engine.TrustedProxies
|
// SetTrustedProxies set a list of network origins (IPv4 addresses,
|
||||||
|
// IPv4 CIDRs, IPv6 addresses or IPv6 CIDRs) from which to trust
|
||||||
|
// request's headers that contain alternative client IP when
|
||||||
|
// `(*gin.Engine).ForwardedByClientIP` is `true`. `TrustedProxies`
|
||||||
|
// feature is enabled by default, and it also trusts all proxies
|
||||||
|
// by default. If you want to disable this feature, use
|
||||||
|
// Engine.SetTrustedProxies(nil), then Context.ClientIP() will
|
||||||
|
// return the remote address directly.
|
||||||
func (engine *Engine) SetTrustedProxies(trustedProxies []string) error {
|
func (engine *Engine) SetTrustedProxies(trustedProxies []string) error {
|
||||||
engine.TrustedProxies = trustedProxies
|
engine.trustedProxies = trustedProxies
|
||||||
return engine.parseTrustedProxies()
|
return engine.parseTrustedProxies()
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseTrustedProxies parse Engine.TrustedProxies to Engine.trustedCIDRs
|
// isUnsafeTrustedProxies compares Engine.trustedCIDRs and defaultTrustedCIDRs, it's not safe if equal (returns true)
|
||||||
|
func (engine *Engine) isUnsafeTrustedProxies() bool {
|
||||||
|
return reflect.DeepEqual(engine.trustedCIDRs, defaultTrustedCIDRs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTrustedProxies parse Engine.trustedProxies to Engine.trustedCIDRs
|
||||||
func (engine *Engine) parseTrustedProxies() error {
|
func (engine *Engine) parseTrustedProxies() error {
|
||||||
trustedCIDRs, err := engine.prepareTrustedCIDRs()
|
trustedCIDRs, err := engine.prepareTrustedCIDRs()
|
||||||
engine.trustedCIDRs = trustedCIDRs
|
engine.trustedCIDRs = trustedCIDRs
|
||||||
@ -415,9 +426,9 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
|
|||||||
debugPrint("Listening and serving HTTPS on %s\n", addr)
|
debugPrint("Listening and serving HTTPS on %s\n", addr)
|
||||||
defer func() { debugPrintError(err) }()
|
defer func() { debugPrintError(err) }()
|
||||||
|
|
||||||
err = engine.parseTrustedProxies()
|
if engine.isUnsafeTrustedProxies() {
|
||||||
if err != nil {
|
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
||||||
return err
|
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
|
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
|
||||||
@ -431,9 +442,9 @@ func (engine *Engine) RunUnix(file string) (err error) {
|
|||||||
debugPrint("Listening and serving HTTP on unix:/%s", file)
|
debugPrint("Listening and serving HTTP on unix:/%s", file)
|
||||||
defer func() { debugPrintError(err) }()
|
defer func() { debugPrintError(err) }()
|
||||||
|
|
||||||
err = engine.parseTrustedProxies()
|
if engine.isUnsafeTrustedProxies() {
|
||||||
if err != nil {
|
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
||||||
return err
|
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
|
||||||
}
|
}
|
||||||
|
|
||||||
listener, err := net.Listen("unix", file)
|
listener, err := net.Listen("unix", file)
|
||||||
@ -454,9 +465,9 @@ func (engine *Engine) RunFd(fd int) (err error) {
|
|||||||
debugPrint("Listening and serving HTTP on fd@%d", fd)
|
debugPrint("Listening and serving HTTP on fd@%d", fd)
|
||||||
defer func() { debugPrintError(err) }()
|
defer func() { debugPrintError(err) }()
|
||||||
|
|
||||||
err = engine.parseTrustedProxies()
|
if engine.isUnsafeTrustedProxies() {
|
||||||
if err != nil {
|
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
||||||
return err
|
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
|
||||||
}
|
}
|
||||||
|
|
||||||
f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
|
f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
|
||||||
@ -475,9 +486,9 @@ func (engine *Engine) RunListener(listener net.Listener) (err error) {
|
|||||||
debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr())
|
debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr())
|
||||||
defer func() { debugPrintError(err) }()
|
defer func() { debugPrintError(err) }()
|
||||||
|
|
||||||
err = engine.parseTrustedProxies()
|
if engine.isUnsafeTrustedProxies() {
|
||||||
if err != nil {
|
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
||||||
return err
|
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = http.Serve(listener, engine)
|
err = http.Serve(listener, engine)
|
||||||
|
@ -76,6 +76,12 @@ func TestRunEmpty(t *testing.T) {
|
|||||||
testRequest(t, "http://localhost:8080/example")
|
testRequest(t, "http://localhost:8080/example")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBadTrustedCIDRs(t *testing.T) {
|
||||||
|
router := New()
|
||||||
|
assert.Error(t, router.SetTrustedProxies([]string{"hello/world"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* legacy tests
|
||||||
func TestBadTrustedCIDRsForRun(t *testing.T) {
|
func TestBadTrustedCIDRsForRun(t *testing.T) {
|
||||||
os.Setenv("PORT", "")
|
os.Setenv("PORT", "")
|
||||||
router := New()
|
router := New()
|
||||||
@ -143,6 +149,7 @@ func TestBadTrustedCIDRsForRunTLS(t *testing.T) {
|
|||||||
router.TrustedProxies = []string{"hello/world"}
|
router.TrustedProxies = []string{"hello/world"}
|
||||||
assert.Error(t, router.RunTLS(":8080", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
|
assert.Error(t, router.RunTLS(":8080", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func TestRunTLS(t *testing.T) {
|
func TestRunTLS(t *testing.T) {
|
||||||
router := New()
|
router := New()
|
||||||
|
57
gin_test.go
57
gin_test.go
@ -539,19 +539,15 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
// valid ipv4 cidr
|
// valid ipv4 cidr
|
||||||
{
|
{
|
||||||
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("0.0.0.0/0")}
|
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("0.0.0.0/0")}
|
||||||
r.TrustedProxies = []string{"0.0.0.0/0"}
|
err := r.SetTrustedProxies([]string{"0.0.0.0/0"})
|
||||||
|
|
||||||
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalid ipv4 cidr
|
// invalid ipv4 cidr
|
||||||
{
|
{
|
||||||
r.TrustedProxies = []string{"192.168.1.33/33"}
|
err := r.SetTrustedProxies([]string{"192.168.1.33/33"})
|
||||||
|
|
||||||
_, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
@ -559,19 +555,16 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
// valid ipv4 address
|
// valid ipv4 address
|
||||||
{
|
{
|
||||||
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("192.168.1.33/32")}
|
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("192.168.1.33/32")}
|
||||||
r.TrustedProxies = []string{"192.168.1.33"}
|
|
||||||
|
|
||||||
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
err := r.SetTrustedProxies([]string{"192.168.1.33"})
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalid ipv4 address
|
// invalid ipv4 address
|
||||||
{
|
{
|
||||||
r.TrustedProxies = []string{"192.168.1.256"}
|
err := r.SetTrustedProxies([]string{"192.168.1.256"})
|
||||||
|
|
||||||
_, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
@ -579,19 +572,15 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
// valid ipv6 address
|
// valid ipv6 address
|
||||||
{
|
{
|
||||||
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("2002:0000:0000:1234:abcd:ffff:c0a8:0101/128")}
|
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("2002:0000:0000:1234:abcd:ffff:c0a8:0101/128")}
|
||||||
r.TrustedProxies = []string{"2002:0000:0000:1234:abcd:ffff:c0a8:0101"}
|
err := r.SetTrustedProxies([]string{"2002:0000:0000:1234:abcd:ffff:c0a8:0101"})
|
||||||
|
|
||||||
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalid ipv6 address
|
// invalid ipv6 address
|
||||||
{
|
{
|
||||||
r.TrustedProxies = []string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101"}
|
err := r.SetTrustedProxies([]string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101"})
|
||||||
|
|
||||||
_, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
@ -599,19 +588,15 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
// valid ipv6 cidr
|
// valid ipv6 cidr
|
||||||
{
|
{
|
||||||
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("::/0")}
|
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("::/0")}
|
||||||
r.TrustedProxies = []string{"::/0"}
|
err := r.SetTrustedProxies([]string{"::/0"})
|
||||||
|
|
||||||
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalid ipv6 cidr
|
// invalid ipv6 cidr
|
||||||
{
|
{
|
||||||
r.TrustedProxies = []string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101/129"}
|
err := r.SetTrustedProxies([]string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101/129"})
|
||||||
|
|
||||||
_, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
@ -623,36 +608,32 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
parseCIDR("192.168.0.0/16"),
|
parseCIDR("192.168.0.0/16"),
|
||||||
parseCIDR("172.16.0.1/32"),
|
parseCIDR("172.16.0.1/32"),
|
||||||
}
|
}
|
||||||
r.TrustedProxies = []string{
|
err := r.SetTrustedProxies([]string{
|
||||||
"::/0",
|
"::/0",
|
||||||
"192.168.0.0/16",
|
"192.168.0.0/16",
|
||||||
"172.16.0.1",
|
"172.16.0.1",
|
||||||
}
|
})
|
||||||
|
|
||||||
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalid combination
|
// invalid combination
|
||||||
{
|
{
|
||||||
r.TrustedProxies = []string{
|
err := r.SetTrustedProxies([]string{
|
||||||
"::/0",
|
"::/0",
|
||||||
"192.168.0.0/16",
|
"192.168.0.0/16",
|
||||||
"172.16.0.256",
|
"172.16.0.256",
|
||||||
}
|
})
|
||||||
_, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nil value
|
// nil value
|
||||||
{
|
{
|
||||||
r.TrustedProxies = nil
|
err := r.SetTrustedProxies(nil)
|
||||||
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
|
||||||
|
|
||||||
assert.Nil(t, trustedCIDRs)
|
assert.Nil(t, r.trustedCIDRs)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
go.mod
6
go.mod
@ -5,9 +5,9 @@ go 1.13
|
|||||||
require (
|
require (
|
||||||
github.com/gin-contrib/sse v0.1.0
|
github.com/gin-contrib/sse v0.1.0
|
||||||
github.com/go-playground/validator/v10 v10.9.0
|
github.com/go-playground/validator/v10 v10.9.0
|
||||||
github.com/goccy/go-json v0.7.8
|
github.com/goccy/go-json v0.7.9
|
||||||
github.com/json-iterator/go v1.1.11
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/mattn/go-isatty v0.0.13
|
github.com/mattn/go-isatty v0.0.14
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/ugorji/go/codec v1.2.6
|
github.com/ugorji/go/codec v1.2.6
|
||||||
google.golang.org/protobuf v1.27.1
|
google.golang.org/protobuf v1.27.1
|
||||||
|
18
go.sum
18
go.sum
@ -12,14 +12,14 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
|
|||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A=
|
github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A=
|
||||||
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||||
github.com/goccy/go-json v0.7.8 h1:CvMH7LotYymYuLGEohBM1lTZWX4g6jzWUUl2aLFuBoE=
|
github.com/goccy/go-json v0.7.9 h1:mSp3uo1tr6MXQTYopSNhHTUnJhd2zQ4Yk+HdJZP+ZRY=
|
||||||
github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.7.9/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
@ -30,12 +30,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
@ -54,9 +54,9 @@ github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxW
|
|||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
@ -118,7 +118,10 @@ func TestMiddlewareNoMethodEnabled(t *testing.T) {
|
|||||||
func TestMiddlewareNoMethodDisabled(t *testing.T) {
|
func TestMiddlewareNoMethodDisabled(t *testing.T) {
|
||||||
signature := ""
|
signature := ""
|
||||||
router := New()
|
router := New()
|
||||||
|
|
||||||
|
// NoMethod disabled
|
||||||
router.HandleMethodNotAllowed = false
|
router.HandleMethodNotAllowed = false
|
||||||
|
|
||||||
router.Use(func(c *Context) {
|
router.Use(func(c *Context) {
|
||||||
signature += "A"
|
signature += "A"
|
||||||
c.Next()
|
c.Next()
|
||||||
@ -144,6 +147,7 @@ func TestMiddlewareNoMethodDisabled(t *testing.T) {
|
|||||||
router.POST("/", func(c *Context) {
|
router.POST("/", func(c *Context) {
|
||||||
signature += " XX "
|
signature += " XX "
|
||||||
})
|
})
|
||||||
|
|
||||||
// RUN
|
// RUN
|
||||||
w := performRequest(router, "GET", "/")
|
w := performRequest(router, "GET", "/")
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ package gin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -60,7 +61,8 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
|
|||||||
// condition that warrants a panic stack trace.
|
// condition that warrants a panic stack trace.
|
||||||
var brokenPipe bool
|
var brokenPipe bool
|
||||||
if ne, ok := err.(*net.OpError); ok {
|
if ne, ok := err.(*net.OpError); ok {
|
||||||
if se, ok := ne.Err.(*os.SyscallError); ok {
|
var se *os.SyscallError
|
||||||
|
if errors.As(ne, &se) {
|
||||||
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
|
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
|
||||||
brokenPipe = true
|
brokenPipe = true
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
testdata "github.com/gin-gonic/gin/testdata/protoexample"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
testdata "github.com/gin-gonic/gin/testdata/protoexample"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO unit tests
|
// TODO unit tests
|
||||||
|
Loading…
x
Reference in New Issue
Block a user