From 89a159bdd92c15374764530ab93477a1740502c1 Mon Sep 17 00:00:00 2001 From: Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com> Date: Wed, 3 Nov 2021 17:13:24 +0300 Subject: [PATCH 01/21] Bump golangci-lint version (#2929) --- .github/workflows/gin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 15c2530a..c25a9091 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -21,7 +21,7 @@ jobs: - name: Setup golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: v1.42.1 + version: v1.43.0 args: --verbose test: needs: lint From 6aee45608da726e4d7e51f558728e0eb90ac6790 Mon Sep 17 00:00:00 2001 From: zero11-0203 <93071220+zero11-0203@users.noreply.github.com> Date: Thu, 11 Nov 2021 08:07:55 +0800 Subject: [PATCH 02/21] Fix grammar (#2933) --- context.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/context.go b/context.go index 58f38c88..2aa91e11 100644 --- a/context.go +++ b/context.go @@ -252,7 +252,7 @@ func (c *Context) Set(key string, value interface{}) { } // Get returns the value for the given key, ie: (value, true). -// If the value does not exists it returns (nil, false) +// If the value does not exist it returns (nil, false) func (c *Context) Get(key string) (value interface{}, exists bool) { c.mu.RLock() value, exists = c.Keys[key] @@ -602,7 +602,7 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error } // Bind checks the Content-Type to select a binding engine automatically, -// Depending the "Content-Type" header different bindings are used: +// Depending on the "Content-Type" header different bindings are used: // "application/json" --> JSON binding // "application/xml" --> XML binding // otherwise --> returns an error. @@ -661,7 +661,7 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error { } // ShouldBind checks the Content-Type to select a binding engine automatically, -// Depending the "Content-Type" header different bindings are used: +// Depending on the "Content-Type" header different bindings are used: // "application/json" --> JSON binding // "application/xml" --> XML binding // otherwise --> returns an error @@ -863,7 +863,7 @@ func (c *Context) Status(code int) { c.Writer.WriteHeader(code) } -// Header is a intelligent shortcut for c.Writer.Header().Set(key, value). +// Header is an intelligent shortcut for c.Writer.Header().Set(key, value). // It writes a header in the response. // If value == "", this method removes the header `c.Writer.Header().Del(key)` func (c *Context) Header(key, value string) { @@ -946,7 +946,7 @@ func (c *Context) HTML(code int, name string, obj interface{}) { // IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body. // It also sets the Content-Type as "application/json". -// WARNING: we recommend to use this only for development purposes since printing pretty JSON is +// WARNING: we recommend using this only for development purposes since printing pretty JSON is // more CPU and bandwidth consuming. Use Context.JSON() instead. func (c *Context) IndentedJSON(code int, obj interface{}) { c.Render(code, render.IndentedJSON{Data: obj}) @@ -1010,7 +1010,7 @@ func (c *Context) String(code int, format string, values ...interface{}) { c.Render(code, render.String{Format: format, Data: values}) } -// Redirect returns a HTTP redirect to the specific location. +// Redirect returns an HTTP redirect to the specific location. func (c *Context) Redirect(code int, location string) { c.Render(-1, render.Redirect{ Code: code, From 57ede9c95abb4bc39f471b101181eb06938b5d7f Mon Sep 17 00:00:00 2001 From: edebernis Date: Sun, 21 Nov 2021 14:45:03 +0100 Subject: [PATCH 03/21] Export struct sliceValidateError to allow error casting and rename it as (#2777) SliceValidationError Co-authored-by: Emeric de Bernis --- binding/default_validator.go | 8 ++++---- binding/default_validator_benchmark_test.go | 4 ++-- binding/default_validator_test.go | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/binding/default_validator.go b/binding/default_validator.go index 87fc4c66..bd8764b1 100644 --- a/binding/default_validator.go +++ b/binding/default_validator.go @@ -18,10 +18,10 @@ type defaultValidator struct { validate *validator.Validate } -type sliceValidateError []error +type SliceValidationError []error -// Error concatenates all error elements in sliceValidateError into a single string separated by \n. -func (err sliceValidateError) Error() string { +// Error concatenates all error elements in SliceValidationError into a single string separated by \n. +func (err SliceValidationError) Error() string { n := len(err) switch n { case 0: @@ -59,7 +59,7 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error { return v.validateStruct(obj) case reflect.Slice, reflect.Array: count := value.Len() - validateRet := make(sliceValidateError, 0) + validateRet := make(SliceValidationError, 0) for i := 0; i < count; i++ { if err := v.ValidateStruct(value.Index(i).Interface()); err != nil { validateRet = append(validateRet, err) diff --git a/binding/default_validator_benchmark_test.go b/binding/default_validator_benchmark_test.go index 839cf710..8d628369 100644 --- a/binding/default_validator_benchmark_test.go +++ b/binding/default_validator_benchmark_test.go @@ -6,10 +6,10 @@ import ( "testing" ) -func BenchmarkSliceValidateError(b *testing.B) { +func BenchmarkSliceValidationError(b *testing.B) { const size int = 100 for i := 0; i < b.N; i++ { - e := make(sliceValidateError, size) + e := make(SliceValidationError, size) for j := 0; j < size; j++ { e[j] = errors.New(strconv.Itoa(j)) } diff --git a/binding/default_validator_test.go b/binding/default_validator_test.go index e9debe59..ff130102 100644 --- a/binding/default_validator_test.go +++ b/binding/default_validator_test.go @@ -9,24 +9,24 @@ import ( "testing" ) -func TestSliceValidateError(t *testing.T) { +func TestSliceValidationError(t *testing.T) { tests := []struct { name string - err sliceValidateError + err SliceValidationError want string }{ - {"has nil elements", sliceValidateError{errors.New("test error"), nil}, "[0]: test error"}, - {"has zero elements", sliceValidateError{}, ""}, - {"has one element", sliceValidateError{errors.New("test one error")}, "[0]: test one error"}, + {"has nil elements", SliceValidationError{errors.New("test error"), nil}, "[0]: test error"}, + {"has zero elements", SliceValidationError{}, ""}, + {"has one element", SliceValidationError{errors.New("test one error")}, "[0]: test one error"}, {"has two elements", - sliceValidateError{ + SliceValidationError{ errors.New("first error"), errors.New("second error"), }, "[0]: first error\n[1]: second error", }, {"has many elements", - sliceValidateError{ + SliceValidationError{ errors.New("first error"), errors.New("second error"), nil, @@ -40,7 +40,7 @@ func TestSliceValidateError(t *testing.T) { 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) + t.Errorf("SliceValidationError.Error() = %v, want %v", got, tt.want) } }) } From 4d7c4ec36ff51c5c3d500f0e77c9e05e5d303a06 Mon Sep 17 00:00:00 2001 From: Notealot <714804968@qq.com> Date: Wed, 24 Nov 2021 21:52:52 +0800 Subject: [PATCH 04/21] chore(docs): Bump to v1.7.7 (#2952) * Perfect TrustedProxies feature * some typo fix * fix some typo * bump to v1.7.6 * remove 'retract' * Revert "remove 'retract'" This reverts commit 3fc2ab44b365d0b14f56ba1a284c52bd88c097b5. * Update CHANGELOG.md * Bump to v1.7.7 preparations --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ go.mod | 2 ++ version.go | 2 +- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 308af74c..4c806a5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Gin ChangeLog +## Gin v1.7.7 + +### BUGFIXES + +* Fixed X-Forwarded-For unsafe handling of CVE-2020-28483 [#2844](https://github.com/gin-gonic/gin/pull/2844), closed issue [#2862](https://github.com/gin-gonic/gin/issues/2862). +* Tree: updated the code logic for `latestNode` [#2897](https://github.com/gin-gonic/gin/pull/2897), closed issue [#2894](https://github.com/gin-gonic/gin/issues/2894) [#2878](https://github.com/gin-gonic/gin/issues/2878). +* Tree: fixed the misplacement of adding slashes [#2847](https://github.com/gin-gonic/gin/pull/2847), closed issue [#2843](https://github.com/gin-gonic/gin/issues/2843). +* Tree: fixed tsr with mixed static and wildcard paths [#2924](https://github.com/gin-gonic/gin/pull/2924), closed issue [#2918](https://github.com/gin-gonic/gin/issues/2918). + +### ENHANCEMENTS + +* TrustedProxies: make it backward-compatible [#2887](https://github.com/gin-gonic/gin/pull/2887), closed issue [#2819](https://github.com/gin-gonic/gin/issues/2819). +* TrustedPlatform: provide custom options for another CDN services [#2906](https://github.com/gin-gonic/gin/pull/2906). + +### DOCS + +* NoMethod: added usage annotation ([#2832](https://github.com/gin-gonic/gin/pull/2832#issuecomment-929954463)). + +## Gin v1.7.6 + +### BUGFIXES + +* bump new release to fix v1.7.5 release error by using v1.7.4 codes. + +## Gin v1.7.4 + +### BUGFIXES + +* bump new release to fix checksum mismatch + ## Gin v1.7.3 ### BUGFIXES diff --git a/go.mod b/go.mod index c25eecf9..8a5d7205 100644 --- a/go.mod +++ b/go.mod @@ -13,3 +13,5 @@ require ( google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 ) + +retract v1.7.5 diff --git a/version.go b/version.go index b9110adb..4b69b9b9 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.7.4" +const Version = "v1.7.7" From 823adfc91abfb2867512b4741a4bebfebfafab64 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Thu, 25 Nov 2021 18:12:08 +0800 Subject: [PATCH 05/21] fix: typo (#2958) --- gin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 1d9c9fee..21513c68 100644 --- a/gin.go +++ b/gin.go @@ -271,7 +271,7 @@ func (engine *Engine) NoMethod(handlers ...HandlerFunc) { engine.rebuild405Handlers() } -// Use attaches a global middleware to the router. ie. the middleware attached though Use() will be +// Use attaches a global middleware to the router. ie. 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. func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { From ffb3b73430e8627e6ea934351930685e15c96619 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Fri, 26 Nov 2021 16:38:10 +0800 Subject: [PATCH 06/21] fix: description error (#2961) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 2aa91e11..a97dd409 100644 --- a/context.go +++ b/context.go @@ -1102,7 +1102,7 @@ type Negotiate struct { Data interface{} } -// Negotiate calls different Render according acceptable Accept format. +// Negotiate calls different Render according to acceptable Accept format. func (c *Context) Negotiate(code int, config Negotiate) { switch c.NegotiateFormat(config.Offered...) { case binding.MIMEJSON: From f068099d0d4b408c8ecbcbe1dc01d700bf5b2bc5 Mon Sep 17 00:00:00 2001 From: minarc Date: Sun, 28 Nov 2021 10:24:14 +0900 Subject: [PATCH 07/21] Update README.md (#2954) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cad746d6..e3d6ab2f 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [http2 server push](#http2-server-push) - [Define format for the log of routes](#define-format-for-the-log-of-routes) - [Set and get a cookie](#set-and-get-a-cookie) - - [Don't trust all proxies](#don't-trust-all-proxies) + - [Don't trust all proxies](#dont-trust-all-proxies) - [Testing](#testing) - [Users](#users) From a06d546f5c2e853b07fde5a41b1fff102829b116 Mon Sep 17 00:00:00 2001 From: Serica <943914044@qq.com> Date: Sun, 28 Nov 2021 09:26:17 +0800 Subject: [PATCH 08/21] prettify error message for catch-all conflict with existing path segment (#2934) --- tree.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 2f0de1a9..521d3ef3 100644 --- a/tree.go +++ b/tree.go @@ -349,7 +349,12 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) } if len(n.path) > 0 && n.path[len(n.path)-1] == '/' { - panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'") + pathSeg := strings.SplitN(n.children[0].path, "/", 2)[0] + panic("catch-all wildcard '" + path + + "' in new path '" + fullPath + + "' conflicts with existing path segment '" + pathSeg + + "' in existing prefix '" + n.path + pathSeg + + "'") } // currently fixed width 1 for '/' From bc2417fc40a8f85fb36305dc05dc6ba3d09182a4 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Tue, 30 Nov 2021 08:36:36 +0800 Subject: [PATCH 09/21] fix: description error (#2968) --- context.go | 2 +- gin.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index a97dd409..30a86982 100644 --- a/context.go +++ b/context.go @@ -739,7 +739,7 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e // It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not. // 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 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 from Request.RemoteAddr) is returned. func (c *Context) ClientIP() string { // Check if we're running on a trusted platform, continue running backwards if error if c.engine.TrustedPlatform != "" { diff --git a/gin.go b/gin.go index 21513c68..d507b51d 100644 --- a/gin.go +++ b/gin.go @@ -102,7 +102,7 @@ type Engine struct { // `(*gin.Context).Request.RemoteAddr`. ForwardedByClientIP bool - // DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.GoogleAppEngine` INSTEAD + // DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD // #726 #755 If enabled, it will trust some headers starting with // 'X-AppEngine...' for better integration with that PaaS. AppEngine bool From 830a63d244ad200a196116f71b6c3c1eda3a538f Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Thu, 2 Dec 2021 18:00:24 +0800 Subject: [PATCH 10/21] fix: typo (#2973) * fix: typo * fix: grammar error --- gin.go | 8 ++++---- logger.go | 4 ++-- recovery.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/gin.go b/gin.go index d507b51d..94de631c 100644 --- a/gin.go +++ b/gin.go @@ -205,7 +205,7 @@ func (engine *Engine) allocateContext() *Context { return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes} } -// Delims sets template left and right delims and returns a Engine instance. +// Delims sets template left and right delims and returns an Engine instance. func (engine *Engine) Delims(left, right string) *Engine { engine.delims = render.Delims{Left: left, Right: right} return engine @@ -259,7 +259,7 @@ func (engine *Engine) SetFuncMap(funcMap template.FuncMap) { engine.FuncMap = funcMap } -// NoRoute adds handlers for NoRoute. It return a 404 code by default. +// NoRoute adds handlers for NoRoute. It returns a 404 code by default. func (engine *Engine) NoRoute(handlers ...HandlerFunc) { engine.noRoute = handlers engine.rebuild404Handlers() @@ -271,7 +271,7 @@ func (engine *Engine) NoMethod(handlers ...HandlerFunc) { engine.rebuild405Handlers() } -// Use attaches a global middleware to the router. ie. the middleware attached through Use() will be +// 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. func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { @@ -442,7 +442,7 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) { } // RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests -// through the specified unix socket (ie. a file). +// through the specified unix socket (i.e. a file). // Note: this method will block the calling goroutine indefinitely unless an error happens. func (engine *Engine) RunUnix(file string) (err error) { debugPrint("Listening and serving HTTP on unix:/%s", file) diff --git a/logger.go b/logger.go index 22138a8d..61b84546 100644 --- a/logger.go +++ b/logger.go @@ -70,7 +70,7 @@ type LogFormatterParams struct { Path string // ErrorMessage is set if error has occurred in processing the request. ErrorMessage string - // isTerm shows whether does gin's output descriptor refers to a terminal. + // isTerm shows whether gin's output descriptor refers to a terminal. isTerm bool // BodySize is the size of the Response Body BodySize int @@ -178,7 +178,7 @@ func ErrorLoggerT(typ ErrorType) HandlerFunc { } // Logger instances a Logger middleware that will write the logs to gin.DefaultWriter. -// By default gin.DefaultWriter = os.Stdout. +// By default, gin.DefaultWriter = os.Stdout. func Logger() HandlerFunc { return LoggerWithConfig(LoggerConfig{}) } diff --git a/recovery.go b/recovery.go index 39f13551..40eba3b2 100644 --- a/recovery.go +++ b/recovery.go @@ -155,7 +155,7 @@ func function(pc uintptr) []byte { // runtime/debug.*T·ptrmethod // and want // *T.ptrmethod - // Also the package path might contains dot (e.g. code.google.com/...), + // Also the package path might contain dot (e.g. code.google.com/...), // so first eliminate the path prefix if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 { name = name[lastSlash+1:] From 0be805a67503c5ad903c57940b7e9c19d88d522a Mon Sep 17 00:00:00 2001 From: Notealot <714804968@qq.com> Date: Fri, 3 Dec 2021 14:49:16 +0800 Subject: [PATCH 11/21] TrustedProxies: Add default IPv6 support and refactor (#2967) --- context.go | 52 ++++++++----------------------------------------- context_test.go | 10 +++++++++- gin.go | 51 ++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 64 insertions(+), 49 deletions(-) diff --git a/context.go b/context.go index 30a86982..4fb3ae6d 100644 --- a/context.go +++ b/context.go @@ -757,10 +757,14 @@ func (c *Context) ClientIP() string { } } - remoteIP, trusted := c.RemoteIP() + // 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 + // defined by Engine.SetTrustedProxies() + remoteIP := net.ParseIP(c.RemoteIP()) if remoteIP == nil { return "" } + trusted := c.engine.isTrustedProxy(remoteIP) if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil { for _, headerName := range c.engine.RemoteIPHeaders { @@ -773,53 +777,13 @@ func (c *Context) ClientIP() string { return remoteIP.String() } -func (e *Engine) isTrustedProxy(ip net.IP) bool { - if e.trustedCIDRs != nil { - for _, cidr := range e.trustedCIDRs { - if cidr.Contains(ip) { - return true - } - } - } - return false -} - // 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. -// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks -// defined by Engine.SetTrustedProxies() -func (c *Context) RemoteIP() (net.IP, bool) { +func (c *Context) RemoteIP() string { ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)) if err != nil { - return nil, false + return "" } - remoteIP := net.ParseIP(ip) - if remoteIP == nil { - return nil, false - } - - return remoteIP, c.engine.isTrustedProxy(remoteIP) -} - -func (e *Engine) validateHeader(header string) (clientIP string, valid bool) { - if header == "" { - return "", false - } - items := strings.Split(header, ",") - for i := len(items) - 1; i >= 0; i-- { - ipStr := strings.TrimSpace(items[i]) - ip := net.ParseIP(ipStr) - if ip == nil { - return "", false - } - - // X-Forwarded-For is appended by proxy - // Check IPs in reverse order and stop when find untrusted proxy - if (i == 0) || (!e.isTrustedProxy(ip)) { - return ipStr, true - } - } - return + return ip } // ContentType returns the Content-Type header of the request. diff --git a/context_test.go b/context_test.go index c286c0f4..4d002c23 100644 --- a/context_test.go +++ b/context_test.go @@ -12,6 +12,7 @@ import ( "html/template" "io" "mime/multipart" + "net" "net/http" "net/http/httptest" "os" @@ -1404,6 +1405,11 @@ func TestContextClientIP(t *testing.T) { // Tests exercising the TrustedProxies functionality resetContextForClientIPTests(c) + // IPv6 support + c.Request.RemoteAddr = "[::1]:12345" + assert.Equal(t, "20.20.20.20", c.ClientIP()) + + resetContextForClientIPTests(c) // No trusted proxies _ = c.engine.SetTrustedProxies([]string{}) c.engine.RemoteIPHeaders = []string{"X-Forwarded-For"} @@ -1500,6 +1506,7 @@ func resetContextForClientIPTests(c *Context) { c.Request.Header.Set("CF-Connecting-IP", "60.60.60.60") c.Request.RemoteAddr = " 40.40.40.40:42123 " c.engine.TrustedPlatform = "" + c.engine.trustedCIDRs = defaultTrustedCIDRs c.engine.AppEngine = false } @@ -2051,7 +2058,8 @@ func TestRemoteIPFail(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.RemoteAddr = "[:::]:80" - ip, trust := c.RemoteIP() + ip := net.ParseIP(c.RemoteIP()) + trust := c.engine.isTrustedProxy(ip) assert.Nil(t, ip) assert.False(t, trust) } diff --git a/gin.go b/gin.go index 94de631c..45f4f2cf 100644 --- a/gin.go +++ b/gin.go @@ -11,7 +11,6 @@ import ( "net/http" "os" "path" - "reflect" "strings" "sync" @@ -28,7 +27,16 @@ var ( 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 +var defaultTrustedCIDRs = []*net.IPNet{ + { // 0.0.0.0/0 (IPv4) + IP: net.IP{0x0, 0x0, 0x0, 0x0}, + Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}, + }, + { // ::/0 (IPv6) + IP: net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + Mask: net.IPMask{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, +} // HandlerFunc defines the handler used by gin middleware as return value. type HandlerFunc func(*Context) @@ -399,9 +407,9 @@ func (engine *Engine) SetTrustedProxies(trustedProxies []string) error { return engine.parseTrustedProxies() } -// isUnsafeTrustedProxies compares Engine.trustedCIDRs and defaultTrustedCIDRs, it's not safe if equal (returns true) +// isUnsafeTrustedProxies checks if Engine.trustedCIDRs contains all IPs, it's not safe if it has (returns true) func (engine *Engine) isUnsafeTrustedProxies() bool { - return reflect.DeepEqual(engine.trustedCIDRs, defaultTrustedCIDRs) + return engine.isTrustedProxy(net.ParseIP("0.0.0.0")) || engine.isTrustedProxy(net.ParseIP("::")) } // parseTrustedProxies parse Engine.trustedProxies to Engine.trustedCIDRs @@ -411,6 +419,41 @@ func (engine *Engine) parseTrustedProxies() error { return err } +// isTrustedProxy will check whether the IP address is included in the trusted list according to Engine.trustedCIDRs +func (engine *Engine) isTrustedProxy(ip net.IP) bool { + if engine.trustedCIDRs == nil { + return false + } + for _, cidr := range engine.trustedCIDRs { + if cidr.Contains(ip) { + return true + } + } + return false +} + +// validateHeader will parse X-Forwarded-For header and return the trusted client IP address +func (engine *Engine) validateHeader(header string) (clientIP string, valid bool) { + if header == "" { + return "", false + } + items := strings.Split(header, ",") + for i := len(items) - 1; i >= 0; i-- { + ipStr := strings.TrimSpace(items[i]) + ip := net.ParseIP(ipStr) + if ip == nil { + break + } + + // X-Forwarded-For is appended by proxy + // Check IPs in reverse order and stop when find untrusted proxy + if (i == 0) || (!engine.isTrustedProxy(ip)) { + return ipStr, true + } + } + return "", false +} + // parseIP parse a string representation of an IP and returns a net.IP with the // minimum byte representation or nil if input is invalid. func parseIP(ip string) net.IP { From 504ec594f8228ead9738ef42c13233eb42ed51d6 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Fri, 3 Dec 2021 14:49:51 +0800 Subject: [PATCH 12/21] fix:typo (#2975) --- gin.go | 2 +- ginS/gins.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gin.go b/gin.go index 45f4f2cf..dac274c2 100644 --- a/gin.go +++ b/gin.go @@ -160,7 +160,7 @@ type Engine struct { var _ IRouter = &Engine{} // New returns a new blank Engine instance without any middleware attached. -// By default the configuration is: +// By default, the configuration is: // - RedirectTrailingSlash: true // - RedirectFixedPath: false // - HandleMethodNotAllowed: false diff --git a/ginS/gins.go b/ginS/gins.go index 3080fd34..ed054bfd 100644 --- a/ginS/gins.go +++ b/ginS/gins.go @@ -37,7 +37,7 @@ func SetHTMLTemplate(templ *template.Template) { engine().SetHTMLTemplate(templ) } -// NoRoute adds handlers for NoRoute. It return a 404 code by default. +// NoRoute adds handlers for NoRoute. It returns a 404 code by default. func NoRoute(handlers ...gin.HandlerFunc) { engine().NoRoute(handlers...) } @@ -118,7 +118,7 @@ func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes { return engine().StaticFS(relativePath, fs) } -// Use attaches a global middleware to the router. ie. the middlewares attached though Use() will be +// Use attaches a global middleware to the router. i.e. the middlewares attached though 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. func Use(middlewares ...gin.HandlerFunc) gin.IRoutes { @@ -145,7 +145,7 @@ func RunTLS(addr, certFile, keyFile string) (err error) { } // RunUnix attaches to a http.Server and starts listening and serving HTTP requests -// through the specified unix socket (ie. a file) +// through the specified unix socket (i.e. a file) // Note: this method will block the calling goroutine indefinitely unless an error happens. func RunUnix(file string) (err error) { return engine().RunUnix(file) From ba7e58989c4eefbaae2aaf4079e9135bafc9b090 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Sun, 5 Dec 2021 08:41:25 +0800 Subject: [PATCH 13/21] fix: typo (#2977) * fix:typo * fix: typo --- gin.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gin.go b/gin.go index dac274c2..54e1a00c 100644 --- a/gin.go +++ b/gin.go @@ -44,7 +44,7 @@ type HandlerFunc func(*Context) // HandlersChain defines a HandlerFunc array. type HandlersChain []HandlerFunc -// Last returns the last handler in the chain. ie. the last handler is the main one. +// Last returns the last handler in the chain. i.e. the last handler is the main one. func (c HandlersChain) Last() HandlerFunc { if length := len(c); length > 0 { return c[length-1] @@ -60,7 +60,7 @@ type RouteInfo struct { HandlerFunc HandlerFunc } -// RoutesInfo defines a RouteInfo array. +// RoutesInfo defines a RouteInfo slice. type RoutesInfo []RouteInfo // Trusted platforms @@ -556,9 +556,9 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { engine.pool.Put(c) } -// HandleContext re-enter a context that has been rewritten. +// HandleContext re-enters a context that has been rewritten. // This can be done by setting c.Request.URL.Path to your new target. -// Disclaimer: You can loop yourself to death with this, use wisely. +// Disclaimer: You can loop yourself to deal with this, use wisely. func (engine *Engine) HandleContext(c *Context) { oldIndexValue := c.index c.reset() From 3973c77263d74d5feb2a5923f2c0cbef866b3cc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Dec 2021 08:09:09 +0800 Subject: [PATCH 14/21] Bump github.com/goccy/go-json from 0.7.10 to 0.8.1 (#2981) Bumps [github.com/goccy/go-json](https://github.com/goccy/go-json) from 0.7.10 to 0.8.1. - [Release notes](https://github.com/goccy/go-json/releases) - [Changelog](https://github.com/goccy/go-json/blob/master/CHANGELOG.md) - [Commits](https://github.com/goccy/go-json/compare/v0.7.10...v0.8.1) --- updated-dependencies: - dependency-name: github.com/goccy/go-json dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8a5d7205..05b9760f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.13 require ( github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.9.0 - github.com/goccy/go-json v0.7.10 + github.com/goccy/go-json v0.8.1 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 51497a0b..adacf5ee 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ 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/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/goccy/go-json v0.7.10 h1:ulhbuNe1JqE68nMRXXTJRrUu0uhouf0VevLINxQq4Ec= -github.com/goccy/go-json v0.7.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI= +github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= From e56d18f19d1f89c68de06e66272373287a4497d3 Mon Sep 17 00:00:00 2001 From: linzi <873804682@qq.com> Date: Sat, 11 Dec 2021 16:24:10 +0800 Subject: [PATCH 15/21] Update README.md (#2985) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e3d6ab2f..f78a64eb 100644 --- a/README.md +++ b/README.md @@ -385,7 +385,7 @@ func main() { router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/upload", func(c *gin.Context) { // single file - file, _ := c.FormFile("file") + file, _ := c.FormFile("Filename") log.Println(file.Filename) // Upload the file to specific dst. From 7d189814cbf6d4c55e8a47b06234736cb1e03457 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Sun, 12 Dec 2021 13:30:33 +0800 Subject: [PATCH 16/21] fix: wrong when wildcard follows named param (#2983) --- tree.go | 2 +- tree_test.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 521d3ef3..a30d3496 100644 --- a/tree.go +++ b/tree.go @@ -535,7 +535,7 @@ walk: // Outer loop for walking the tree // No handle found. Check if a handle for this path + a // trailing slash exists for TSR recommendation n = n.children[0] - value.tsr = n.path == "/" && n.handlers != nil + value.tsr = (n.path == "/" && n.handlers != nil) || (n.path == "" && n.indices == "/") } return diff --git a/tree_test.go b/tree_test.go index c3723396..94c53386 100644 --- a/tree_test.go +++ b/tree_test.go @@ -595,6 +595,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) { "/blog/:p", "/posts/:b/:c", "/posts/b/:c/d/", + "/vendor/:x/*y", } for _, route := range routes { recv := catchPanic(func() { @@ -631,6 +632,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) { "/api/world/abc/", "/blog/pp/", "/posts/b/c/d", + "/vendor/x", } for _, route := range tsrRoutes { From fb5f0454178756d8040f9224092319febc3853b8 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Wed, 15 Dec 2021 23:27:23 +0800 Subject: [PATCH 17/21] fix: description error (#2986) --- AUTHORS.md | 5 +++++ gin.go | 2 +- mode.go | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index c634e6be..533204ed 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -109,6 +109,11 @@ People and companies, who have contributed, in alphabetical order. - Fix typo in comment +**@jincheng9 (Jincheng Zhang)** +- ★ support TSR when wildcard follows named param +- Fix errors and typos in comments + + **@joiggama (Ignacio Galindo)** - Add utf-8 charset header on renders diff --git a/gin.go b/gin.go index 54e1a00c..9bd4c46a 100644 --- a/gin.go +++ b/gin.go @@ -41,7 +41,7 @@ var defaultTrustedCIDRs = []*net.IPNet{ // HandlerFunc defines the handler used by gin middleware as return value. type HandlerFunc func(*Context) -// HandlersChain defines a HandlerFunc array. +// HandlersChain defines a HandlerFunc slice. type HandlersChain []HandlerFunc // Last returns the last handler in the chain. i.e. the last handler is the main one. diff --git a/mode.go b/mode.go index 4d199df3..1fb994b4 100644 --- a/mode.go +++ b/mode.go @@ -88,7 +88,7 @@ func EnableJsonDecoderDisallowUnknownFields() { binding.EnableDecoderDisallowUnknownFields = true } -// Mode returns currently gin mode. +// Mode returns current gin mode. func Mode() string { return modeName } From 92a988d7613259f4edce0964a7244fc55a9ea015 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Sat, 18 Dec 2021 19:40:47 +0800 Subject: [PATCH 18/21] fix: description error (#2988) * fix:typo * fix: typo * fix: wrong when wildcard follows named param * fix: description error * update author list * fix:typo * fix: description error --- context.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index 4fb3ae6d..231bb142 100644 --- a/context.go +++ b/context.go @@ -59,7 +59,7 @@ type Context struct { params *Params skippedNodes *[]skippedNode - // This mutex protect Keys map + // This mutex protects Keys map. mu sync.RWMutex // Keys is a key/value pair exclusively for the context of each request. @@ -71,10 +71,10 @@ type Context struct { // Accepted defines a list of manually accepted formats for content negotiation. Accepted []string - // queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query() + // queryCache caches the query result from c.Request.URL.Query(). queryCache url.Values - // formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH, + // formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH, // or PUT body parameters. formCache url.Values From 8a0f95cc71b9da1a645ad5075c1d2e256d9fa572 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Mon, 20 Dec 2021 17:42:54 +0800 Subject: [PATCH 19/21] fix: typo (#2993) --- binding/binding.go | 2 +- binding/binding_nomsgpack.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/binding/binding.go b/binding/binding.go index deb71661..0414a345 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -40,7 +40,7 @@ type BindingBody interface { } // BindingUri adds BindUri method to Binding. BindUri is similar with Bind, -// but it read the Params. +// but it reads the Params. type BindingUri interface { Name() string BindUri(map[string][]string, interface{}) error diff --git a/binding/binding_nomsgpack.go b/binding/binding_nomsgpack.go index 23424470..f0b667b2 100644 --- a/binding/binding_nomsgpack.go +++ b/binding/binding_nomsgpack.go @@ -38,7 +38,7 @@ type BindingBody interface { } // BindingUri adds BindUri method to Binding. BindUri is similar with Bind, -// but it read the Params. +// but it reads the Params. type BindingUri interface { Name() string BindUri(map[string][]string, interface{}) error From 1538ece13fd37d7ecdc541c9b561e48ff14c1615 Mon Sep 17 00:00:00 2001 From: linzi <873804682@qq.com> Date: Thu, 23 Dec 2021 07:51:12 +0800 Subject: [PATCH 20/21] Update README.md (#2997) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f78a64eb..6ae567b3 100644 --- a/README.md +++ b/README.md @@ -384,7 +384,7 @@ func main() { // Set a lower memory limit for multipart forms (default is 32 MiB) router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/upload", func(c *gin.Context) { - // single file + // Single file file, _ := c.FormFile("Filename") log.Println(file.Filename) @@ -417,7 +417,7 @@ func main() { router.POST("/upload", func(c *gin.Context) { // Multipart form form, _ := c.MultipartForm() - files := form.File["upload[]"] + files := form.File["Filename[]"] for _, file := range files { log.Println(file.Filename) From d062a6a6155236883f4c3292379ab94b1eac8b05 Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Sun, 26 Dec 2021 08:02:01 +0800 Subject: [PATCH 21/21] docs: redirect to the correct line of code (#2998) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ae567b3..b75df63f 100644 --- a/README.md +++ b/README.md @@ -906,7 +906,7 @@ func startPage(c *gin.Context) { var person Person // If `GET`, only `Form` binding engine (`query`) used. // If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`). - // See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48 + // See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L88 if c.ShouldBind(&person) == nil { log.Println(person.Name) log.Println(person.Address)