mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-18 06:42:10 +08:00
Merge branch 'master' into feature/optimize-20210525
This commit is contained in:
commit
02cf488701
@ -71,7 +71,7 @@ func (v *defaultValidator) validateStruct(obj interface{}) error {
|
|||||||
// 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 -
|
||||||
// https://godoc.org/gopkg.in/go-playground/validator.v8
|
// https://pkg.go.dev/github.com/go-playground/validator/v10
|
||||||
func (v *defaultValidator) Engine() interface{} {
|
func (v *defaultValidator) Engine() interface{} {
|
||||||
v.lazyinit()
|
v.lazyinit()
|
||||||
return v.validate
|
return v.validate
|
||||||
|
@ -731,10 +731,15 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e
|
|||||||
// If the headers are nots syntactically valid OR the remote IP does not correspong to a trusted proxy,
|
// If the headers are nots syntactically valid OR the remote IP does not correspong 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 {
|
||||||
if c.engine.AppEngine {
|
switch {
|
||||||
|
case c.engine.AppEngine:
|
||||||
if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" {
|
if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" {
|
||||||
return addr
|
return addr
|
||||||
}
|
}
|
||||||
|
case c.engine.CloudflareProxy:
|
||||||
|
if addr := c.requestHeader("CF-Connecting-IP"); addr != "" {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteIP, trusted := c.RemoteIP()
|
remoteIP, trusted := c.RemoteIP()
|
||||||
|
@ -1392,14 +1392,10 @@ func TestContextAbortWithError(t *testing.T) {
|
|||||||
assert.True(t, c.IsAborted())
|
assert.True(t, c.IsAborted())
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetTrustedCIDRs(c *Context) {
|
|
||||||
c.engine.trustedCIDRs, _ = c.engine.prepareTrustedCIDRs()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContextClientIP(t *testing.T) {
|
func TestContextClientIP(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||||
resetTrustedCIDRs(c)
|
c.engine.trustedCIDRs, _ = c.engine.prepareTrustedCIDRs()
|
||||||
resetContextForClientIPTests(c)
|
resetContextForClientIPTests(c)
|
||||||
|
|
||||||
// Legacy tests (validating that the defaults don't break the
|
// Legacy tests (validating that the defaults don't break the
|
||||||
@ -1428,57 +1424,47 @@ func TestContextClientIP(t *testing.T) {
|
|||||||
resetContextForClientIPTests(c)
|
resetContextForClientIPTests(c)
|
||||||
|
|
||||||
// No trusted proxies
|
// No trusted proxies
|
||||||
c.engine.TrustedProxies = []string{}
|
_ = c.engine.SetTrustedProxies([]string{})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
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())
|
||||||
|
|
||||||
// Last proxy is trusted, but the RemoteAddr is not
|
// Last proxy is trusted, but the RemoteAddr is not
|
||||||
c.engine.TrustedProxies = []string{"30.30.30.30"}
|
_ = c.engine.SetTrustedProxies([]string{"30.30.30.30"})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
|
||||||
// Only trust RemoteAddr
|
// Only trust RemoteAddr
|
||||||
c.engine.TrustedProxies = []string{"40.40.40.40"}
|
_ = c.engine.SetTrustedProxies([]string{"40.40.40.40"})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
assert.Equal(t, "20.20.20.20", c.ClientIP())
|
assert.Equal(t, "20.20.20.20", c.ClientIP())
|
||||||
|
|
||||||
// All steps are trusted
|
// All steps are trusted
|
||||||
c.engine.TrustedProxies = []string{"40.40.40.40", "30.30.30.30", "20.20.20.20"}
|
_ = c.engine.SetTrustedProxies([]string{"40.40.40.40", "30.30.30.30", "20.20.20.20"})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
assert.Equal(t, "20.20.20.20", c.ClientIP())
|
assert.Equal(t, "20.20.20.20", c.ClientIP())
|
||||||
|
|
||||||
// Use CIDR
|
// Use CIDR
|
||||||
c.engine.TrustedProxies = []string{"40.40.25.25/16", "30.30.30.30"}
|
_ = c.engine.SetTrustedProxies([]string{"40.40.25.25/16", "30.30.30.30"})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
assert.Equal(t, "20.20.20.20", c.ClientIP())
|
assert.Equal(t, "20.20.20.20", c.ClientIP())
|
||||||
|
|
||||||
// Use hostname that resolves to all the proxies
|
// Use hostname that resolves to all the proxies
|
||||||
c.engine.TrustedProxies = []string{"foo"}
|
_ = c.engine.SetTrustedProxies([]string{"foo"})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
|
||||||
// Use hostname that returns an error
|
// Use hostname that returns an error
|
||||||
c.engine.TrustedProxies = []string{"bar"}
|
_ = c.engine.SetTrustedProxies([]string{"bar"})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
|
||||||
// X-Forwarded-For has a non-IP element
|
// X-Forwarded-For has a non-IP element
|
||||||
c.engine.TrustedProxies = []string{"40.40.40.40"}
|
_ = c.engine.SetTrustedProxies([]string{"40.40.40.40"})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
c.Request.Header.Set("X-Forwarded-For", " blah ")
|
c.Request.Header.Set("X-Forwarded-For", " blah ")
|
||||||
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
|
||||||
// Result from LookupHost has non-IP element. This should never
|
// Result from LookupHost has non-IP element. This should never
|
||||||
// happen, but we should test it to make sure we handle it
|
// happen, but we should test it to make sure we handle it
|
||||||
// gracefully.
|
// gracefully.
|
||||||
c.engine.TrustedProxies = []string{"baz"}
|
_ = c.engine.SetTrustedProxies([]string{"baz"})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
c.Request.Header.Set("X-Forwarded-For", " 30.30.30.30 ")
|
c.Request.Header.Set("X-Forwarded-For", " 30.30.30.30 ")
|
||||||
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
|
||||||
c.engine.TrustedProxies = []string{"40.40.40.40"}
|
_ = c.engine.SetTrustedProxies([]string{"40.40.40.40"})
|
||||||
resetTrustedCIDRs(c)
|
|
||||||
c.Request.Header.Del("X-Forwarded-For")
|
c.Request.Header.Del("X-Forwarded-For")
|
||||||
c.engine.RemoteIPHeaders = []string{"X-Forwarded-For", "X-Real-IP"}
|
c.engine.RemoteIPHeaders = []string{"X-Forwarded-For", "X-Real-IP"}
|
||||||
assert.Equal(t, "10.10.10.10", c.ClientIP())
|
assert.Equal(t, "10.10.10.10", c.ClientIP())
|
||||||
@ -1490,6 +1476,13 @@ func TestContextClientIP(t *testing.T) {
|
|||||||
c.Request.Header.Del("X-Appengine-Remote-Addr")
|
c.Request.Header.Del("X-Appengine-Remote-Addr")
|
||||||
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
|
||||||
|
c.engine.AppEngine = false
|
||||||
|
c.engine.CloudflareProxy = true
|
||||||
|
assert.Equal(t, "60.60.60.60", c.ClientIP())
|
||||||
|
|
||||||
|
c.Request.Header.Del("CF-Connecting-IP")
|
||||||
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
||||||
|
|
||||||
// no port
|
// no port
|
||||||
c.Request.RemoteAddr = "50.50.50.50"
|
c.Request.RemoteAddr = "50.50.50.50"
|
||||||
assert.Empty(t, c.ClientIP())
|
assert.Empty(t, c.ClientIP())
|
||||||
@ -1499,6 +1492,7 @@ func resetContextForClientIPTests(c *Context) {
|
|||||||
c.Request.Header.Set("X-Real-IP", " 10.10.10.10 ")
|
c.Request.Header.Set("X-Real-IP", " 10.10.10.10 ")
|
||||||
c.Request.Header.Set("X-Forwarded-For", " 20.20.20.20, 30.30.30.30")
|
c.Request.Header.Set("X-Forwarded-For", " 20.20.20.20, 30.30.30.30")
|
||||||
c.Request.Header.Set("X-Appengine-Remote-Addr", "50.50.50.50")
|
c.Request.Header.Set("X-Appengine-Remote-Addr", "50.50.50.50")
|
||||||
|
c.Request.Header.Set("CF-Connecting-IP", "60.60.60.60")
|
||||||
c.Request.RemoteAddr = " 40.40.40.40:42123 "
|
c.Request.RemoteAddr = " 40.40.40.40:42123 "
|
||||||
c.engine.AppEngine = false
|
c.engine.AppEngine = false
|
||||||
}
|
}
|
||||||
|
42
gin.go
42
gin.go
@ -105,6 +105,10 @@ type Engine struct {
|
|||||||
// 'X-AppEngine...' for better integration with that PaaS.
|
// 'X-AppEngine...' for better integration with that PaaS.
|
||||||
AppEngine bool
|
AppEngine bool
|
||||||
|
|
||||||
|
// If enabled, it will trust the CF-Connecting-IP header to determine the
|
||||||
|
// IP of the client.
|
||||||
|
CloudflareProxy bool
|
||||||
|
|
||||||
// If enabled, the url.RawPath will be used to find parameters.
|
// If enabled, the url.RawPath will be used to find parameters.
|
||||||
UseRawPath bool
|
UseRawPath bool
|
||||||
|
|
||||||
@ -326,11 +330,11 @@ 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) }()
|
||||||
|
|
||||||
trustedCIDRs, err := engine.prepareTrustedCIDRs()
|
err = engine.parseTrustedProxies()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
engine.trustedCIDRs = trustedCIDRs
|
|
||||||
address := resolveAddress(addr)
|
address := resolveAddress(addr)
|
||||||
debugPrint("Listening and serving HTTP on %s\n", address)
|
debugPrint("Listening and serving HTTP on %s\n", address)
|
||||||
err = http.ListenAndServe(address, engine)
|
err = http.ListenAndServe(address, engine)
|
||||||
@ -366,6 +370,19 @@ func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
|
|||||||
return cidr, nil
|
return cidr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetTrustedProxies set Engine.TrustedProxies
|
||||||
|
func (engine *Engine) SetTrustedProxies(trustedProxies []string) error {
|
||||||
|
engine.TrustedProxies = trustedProxies
|
||||||
|
return engine.parseTrustedProxies()
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTrustedProxies parse Engine.TrustedProxies to Engine.trustedCIDRs
|
||||||
|
func (engine *Engine) parseTrustedProxies() error {
|
||||||
|
trustedCIDRs, err := engine.prepareTrustedCIDRs()
|
||||||
|
engine.trustedCIDRs = trustedCIDRs
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// parseIP parse a string representation of an IP and returns a net.IP with the
|
// parseIP parse a string representation of an IP and returns a net.IP with the
|
||||||
// minimum byte representation or nil if input is invalid.
|
// minimum byte representation or nil if input is invalid.
|
||||||
func parseIP(ip string) net.IP {
|
func parseIP(ip string) net.IP {
|
||||||
@ -387,6 +404,11 @@ 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 err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
|
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -398,6 +420,11 @@ 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 err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
listener, err := net.Listen("unix", file)
|
listener, err := net.Listen("unix", file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -416,6 +443,11 @@ 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 err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
|
f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
|
||||||
listener, err := net.FileListener(f)
|
listener, err := net.FileListener(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -431,6 +463,12 @@ func (engine *Engine) RunFd(fd int) (err error) {
|
|||||||
func (engine *Engine) RunListener(listener net.Listener) (err error) {
|
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 err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err = http.Serve(listener, engine)
|
err = http.Serve(listener, engine)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -55,13 +55,74 @@ func TestRunEmpty(t *testing.T) {
|
|||||||
testRequest(t, "http://localhost:8080/example")
|
testRequest(t, "http://localhost:8080/example")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTrustedCIDRsForRun(t *testing.T) {
|
func TestBadTrustedCIDRsForRun(t *testing.T) {
|
||||||
os.Setenv("PORT", "")
|
os.Setenv("PORT", "")
|
||||||
router := New()
|
router := New()
|
||||||
router.TrustedProxies = []string{"hello/world"}
|
router.TrustedProxies = []string{"hello/world"}
|
||||||
assert.Error(t, router.Run(":8080"))
|
assert.Error(t, router.Run(":8080"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBadTrustedCIDRsForRunUnix(t *testing.T) {
|
||||||
|
router := New()
|
||||||
|
router.TrustedProxies = []string{"hello/world"}
|
||||||
|
|
||||||
|
unixTestSocket := filepath.Join(os.TempDir(), "unix_unit_test")
|
||||||
|
|
||||||
|
defer os.Remove(unixTestSocket)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||||
|
assert.Error(t, router.RunUnix(unixTestSocket))
|
||||||
|
}()
|
||||||
|
// have to wait for the goroutine to start and run the server
|
||||||
|
// otherwise the main thread will complete
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadTrustedCIDRsForRunFd(t *testing.T) {
|
||||||
|
router := New()
|
||||||
|
router.TrustedProxies = []string{"hello/world"}
|
||||||
|
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
listener, err := net.ListenTCP("tcp", addr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
socketFile, err := listener.File()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||||
|
assert.Error(t, router.RunFd(int(socketFile.Fd())))
|
||||||
|
}()
|
||||||
|
// have to wait for the goroutine to start and run the server
|
||||||
|
// otherwise the main thread will complete
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadTrustedCIDRsForRunListener(t *testing.T) {
|
||||||
|
router := New()
|
||||||
|
router.TrustedProxies = []string{"hello/world"}
|
||||||
|
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
listener, err := net.ListenTCP("tcp", addr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
go func() {
|
||||||
|
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||||
|
assert.Error(t, router.RunListener(listener))
|
||||||
|
}()
|
||||||
|
// have to wait for the goroutine to start and run the server
|
||||||
|
// otherwise the main thread will complete
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadTrustedCIDRsForRunTLS(t *testing.T) {
|
||||||
|
os.Setenv("PORT", "")
|
||||||
|
router := New()
|
||||||
|
router.TrustedProxies = []string{"hello/world"}
|
||||||
|
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()
|
||||||
go func() {
|
go func() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user