From dd8a27c0b6fbbfd192c5586ba3165700f535ac7f Mon Sep 17 00:00:00 2001 From: "Alessandro (Ale) Segala" <43508+ItalyPaleAle@users.noreply.github.com> Date: Wed, 23 Jun 2021 17:58:10 -0700 Subject: [PATCH 001/346] Setting trusted platform using an enum-like (#2739) --- context.go | 16 +++++++++++++--- context_appengine.go | 2 +- context_test.go | 15 ++++++++++++--- gin.go | 23 +++++++++++++++++------ 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/context.go b/context.go index 0c1fb07f48..5c1aba5e61 100644 --- a/context.go +++ b/context.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "io/ioutil" + "log" "math" "mime/multipart" "net" @@ -731,17 +732,26 @@ 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, // the remote IP (coming form Request.RemoteAddr) is returned. func (c *Context) ClientIP() string { - switch { - case c.engine.AppEngine: + // Check if we're running on a tursted platform + switch c.engine.TrustedPlatform { + case PlatformGoogleAppEngine: if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { return addr } - case c.engine.CloudflareProxy: + case PlatformCloudflare: if addr := c.requestHeader("CF-Connecting-IP"); addr != "" { return addr } } + // Legacy "AppEngine" flag + if c.engine.AppEngine { + log.Println(`The AppEngine flag is going to be deprecated. Please check issues #2723 and #2739 and use 'TrustedPlatform: gin.PlatformGoogleAppEngine' instead.`) + if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { + return addr + } + } + remoteIP, trusted := c.RemoteIP() if remoteIP == nil { return "" diff --git a/context_appengine.go b/context_appengine.go index d565843489..8bf938961d 100644 --- a/context_appengine.go +++ b/context_appengine.go @@ -8,5 +8,5 @@ package gin func init() { - defaultAppEngine = true + defaultPlatform = PlatformGoogleAppEngine } diff --git a/context_test.go b/context_test.go index e0de717ea7..93b58ee8e1 100644 --- a/context_test.go +++ b/context_test.go @@ -1410,7 +1410,7 @@ func TestContextClientIP(t *testing.T) { c.Request.Header.Del("X-Forwarded-For") c.Request.Header.Del("X-Real-IP") - c.engine.AppEngine = true + c.engine.TrustedPlatform = PlatformGoogleAppEngine assert.Equal(t, "50.50.50.50", c.ClientIP()) c.Request.Header.Del("X-Appengine-Remote-Addr") @@ -1470,19 +1470,27 @@ func TestContextClientIP(t *testing.T) { assert.Equal(t, "10.10.10.10", c.ClientIP()) c.engine.RemoteIPHeaders = []string{} + c.engine.TrustedPlatform = PlatformGoogleAppEngine + assert.Equal(t, "50.50.50.50", c.ClientIP()) + + // Test the legacy flag + c.engine.TrustedPlatform = "" c.engine.AppEngine = true assert.Equal(t, "50.50.50.50", c.ClientIP()) + c.engine.AppEngine = false + c.engine.TrustedPlatform = PlatformGoogleAppEngine c.Request.Header.Del("X-Appengine-Remote-Addr") assert.Equal(t, "40.40.40.40", c.ClientIP()) - c.engine.AppEngine = false - c.engine.CloudflareProxy = true + c.engine.TrustedPlatform = PlatformCloudflare 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()) + c.engine.TrustedPlatform = "" + // no port c.Request.RemoteAddr = "50.50.50.50" assert.Empty(t, c.ClientIP()) @@ -1494,6 +1502,7 @@ func resetContextForClientIPTests(c *Context) { 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.engine.TrustedPlatform = "" c.engine.AppEngine = false } diff --git a/gin.go b/gin.go index 00686e77e1..fd4fa51241 100644 --- a/gin.go +++ b/gin.go @@ -25,7 +25,7 @@ var ( default405Body = []byte("405 method not allowed") ) -var defaultAppEngine bool +var defaultPlatform string // HandlerFunc defines the handler used by gin middleware as return value. type HandlerFunc func(*Context) @@ -52,6 +52,16 @@ type RouteInfo struct { // RoutesInfo defines a RouteInfo array. type RoutesInfo []RouteInfo +// Trusted platforms +const ( + // When running on Google App Engine. Trust X-Appengine-Remote-Addr + // for determining the client's IP + PlatformGoogleAppEngine = "google-app-engine" + // When using Cloudflare's CDN. Trust CF-Connecting-IP for determining + // the client's IP + PlatformCloudflare = "cloudflare" +) + // Engine is the framework's instance, it contains the muxer, middleware and configuration settings. // Create an instance of Engine, by using New() or Default() type Engine struct { @@ -101,14 +111,15 @@ type Engine struct { // `true`. TrustedProxies []string + // If set to a constant of value gin.Platform*, trusts the headers set by + // that platform, for example to determine the client IP + TrustedPlatform string + + // DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.GoogleAppEngine` INSTEAD // #726 #755 If enabled, it will trust some headers starting with // 'X-AppEngine...' for better integration with that PaaS. 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. UseRawPath bool @@ -164,7 +175,7 @@ func New() *Engine { ForwardedByClientIP: true, RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"}, TrustedProxies: []string{"0.0.0.0/0"}, - AppEngine: defaultAppEngine, + TrustedPlatform: defaultPlatform, UseRawPath: false, RemoveExtraSlash: false, UnescapePathValues: true, From be860ec1578f0c9d1e220934455e9507d3a4c1e6 Mon Sep 17 00:00:00 2001 From: ziheng Date: Thu, 24 Jun 2021 13:07:49 +0800 Subject: [PATCH 002/346] fix typo and add comments (#2760) --- recovery.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/recovery.go b/recovery.go index 563f5aaa8e..8519959141 100644 --- a/recovery.go +++ b/recovery.go @@ -34,7 +34,7 @@ func Recovery() HandlerFunc { return RecoveryWithWriter(DefaultErrorWriter) } -//CustomRecovery returns a middleware that recovers from any panics and calls the provided handle func to handle it. +// CustomRecovery returns a middleware that recovers from any panics and calls the provided handle func to handle it. func CustomRecovery(handle RecoveryFunc) HandlerFunc { return RecoveryWithWriter(DefaultErrorWriter, handle) } @@ -165,6 +165,7 @@ func function(pc uintptr) []byte { return name } +// timeFormat returns a customized time string for logger. func timeFormat(t time.Time) string { timeString := t.Format("2006/01/02 - 15:04:05") return timeString From 09f6cff92a319b080789c8d20aa4cc6efbe876fe Mon Sep 17 00:00:00 2001 From: ziheng Date: Thu, 24 Jun 2021 15:31:38 +0800 Subject: [PATCH 003/346] skip unnecessary variable assignment in timeFormat (#2761) --- recovery.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/recovery.go b/recovery.go index 8519959141..3101fe28a0 100644 --- a/recovery.go +++ b/recovery.go @@ -167,6 +167,5 @@ func function(pc uintptr) []byte { // timeFormat returns a customized time string for logger. func timeFormat(t time.Time) string { - timeString := t.Format("2006/01/02 - 15:04:05") - return timeString + return t.Format("2006/01/02 - 15:04:05") } From 7834a03e8467c1b58eda0efd58374896f240f69c Mon Sep 17 00:00:00 2001 From: wei Date: Thu, 24 Jun 2021 16:33:14 +0800 Subject: [PATCH 004/346] gin.Context with fallback value from gin.Context.Request.Context() (#2751) * Update tree.go (#2659) delete more "()" * updated comments for Get function for params (#2756) * ci: add github action workflows (#2596) * ci: add github action workflows * test: fixed the TestUnixSocket test on windows (#20) * ci: add github action workflows (#18) * Remove .travis.yml * ci: replace GITTER_ROOM_ID and upload coverage every time you go test * ci: update coverage using codecov/codecov-action@v1 * Merge branch 'master' into github-actions * repo: replace travis ci to github actions * ci: add go version 1.16 * fix: go install requires a specific version * chore(ci): remove go 1.12 support * chore(ci): remove os windows-latest Co-authored-by: thinkerou Co-authored-by: Bo-Yi Wu * Setting trusted platform using an enum-like (#2739) * gin.Context with fallback value from c.Request.Context() * add test case Co-authored-by: youzeliang Co-authored-by: Ashwani Co-authored-by: Jeff Co-authored-by: thinkerou Co-authored-by: Bo-Yi Wu Co-authored-by: Alessandro (Ale) Segala <43508+ItalyPaleAle@users.noreply.github.com> --- context.go | 10 +++++++--- context_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index 5c1aba5e61..ede16fd4d8 100644 --- a/context.go +++ b/context.go @@ -1186,8 +1186,12 @@ func (c *Context) Value(key interface{}) interface{} { return c.Request } if keyAsString, ok := key.(string); ok { - val, _ := c.Get(keyAsString) - return val + if val, exists := c.Get(keyAsString); exists { + return val + } } - return nil + if c.Request == nil || c.Request.Context() == nil { + return nil + } + return c.Request.Context().Value(key) } diff --git a/context_test.go b/context_test.go index 93b58ee8e1..2a4d21856e 100644 --- a/context_test.go +++ b/context_test.go @@ -2057,3 +2057,56 @@ func TestRemoteIPFail(t *testing.T) { assert.Nil(t, ip) assert.False(t, trust) } + +func TestContextWithFallbackValueFromRequestContext(t *testing.T) { + tests := []struct { + name string + getContextAndKey func() (*Context, interface{}) + value interface{} + }{ + { + name: "c with struct context key", + getContextAndKey: func() (*Context, interface{}) { + var key struct{} + c := &Context{} + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request = c.Request.WithContext(context.WithValue(context.TODO(), key, "value")) + return c, key + }, + value: "value", + }, + { + name: "c with string context key", + getContextAndKey: func() (*Context, interface{}) { + c := &Context{} + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request = c.Request.WithContext(context.WithValue(context.TODO(), "key", "value")) + return c, "key" + }, + value: "value", + }, + { + name: "c with nil http.Request", + getContextAndKey: func() (*Context, interface{}) { + c := &Context{} + return c, "key" + }, + value: nil, + }, + { + name: "c with nil http.Request.Context()", + getContextAndKey: func() (*Context, interface{}) { + c := &Context{} + c.Request, _ = http.NewRequest("POST", "/", nil) + return c, "key" + }, + value: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c, key := tt.getContextAndKey() + assert.Equal(t, tt.value, c.Value(key)) + }) + } +} From f2bbdfe9f26d84cb994f381050692a9e4553bf75 Mon Sep 17 00:00:00 2001 From: ziheng Date: Fri, 25 Jun 2021 12:14:06 +0800 Subject: [PATCH 005/346] Use buf.String() instead of string(buf.Bytes()) (#2764) --- render/msgpack.go | 2 ++ render/render_msgpack_test.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/render/msgpack.go b/render/msgpack.go index 6ef5b6e514..7f17ca4d9c 100644 --- a/render/msgpack.go +++ b/render/msgpack.go @@ -13,6 +13,8 @@ import ( "github.com/ugorji/go/codec" ) +// Check interface implemented here to support go build tag nomsgpack. +// See: https://github.com/gin-gonic/gin/pull/1852/ var ( _ Render = MsgPack{} ) diff --git a/render/render_msgpack_test.go b/render/render_msgpack_test.go index 8170fbe815..d16cf6e6e2 100644 --- a/render/render_msgpack_test.go +++ b/render/render_msgpack_test.go @@ -39,6 +39,6 @@ func TestRenderMsgPack(t *testing.T) { err = codec.NewEncoder(buf, h).Encode(data) assert.NoError(t, err) - assert.Equal(t, w.Body.String(), string(buf.Bytes())) + assert.Equal(t, w.Body.String(), buf.String()) assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type")) } From 1d0f938f28d7e592623264591605dc22ac141cc5 Mon Sep 17 00:00:00 2001 From: raymonder jin Date: Fri, 25 Jun 2021 13:22:01 +0800 Subject: [PATCH 006/346] Fix insufficient slice check (#2755) --- tree.go | 2 +- tree_test.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 5f35fff473..4b5656fb08 100644 --- a/tree.go +++ b/tree.go @@ -459,7 +459,7 @@ walk: // Outer loop for walking the tree } // Save param value - if params != nil { + if params != nil && cap(*params) > 0 { if value.params == nil { value.params = params } diff --git a/tree_test.go b/tree_test.go index 298c5ed0c5..7459317fa3 100644 --- a/tree_test.go +++ b/tree_test.go @@ -717,6 +717,19 @@ func TestTreeInvalidNodeType(t *testing.T) { } } +func TestTreeInvalidParamsType(t *testing.T) { + tree := &node{} + tree.wildChild = true + tree.children = append(tree.children, &node{}) + tree.children[0].nType = 2 + + // set invalid Params type + params := make(Params, 0, 0) + + // try to trigger slice bounds out of range with capacity 0 + tree.getValue("/test", ¶ms, false) +} + func TestTreeWildcardConflictEx(t *testing.T) { conflicts := [...]struct { route string From e3ee01d1850b018b24ac215a3bb8de3d79e2604b Mon Sep 17 00:00:00 2001 From: ziheng Date: Mon, 28 Jun 2021 22:05:29 +0800 Subject: [PATCH 007/346] improve sliceValidateError.Error performance using switch and strings.Builder (#2765) fix missing nil pointer check use simpler switch case add missing tests use for-loop instead of range add benchmark test codes --- binding/default_validator.go | 24 +++++++++++++++------ binding/default_validator_benchmark_test.go | 20 +++++++++++++++++ binding/default_validator_test.go | 20 +++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 binding/default_validator_benchmark_test.go diff --git a/binding/default_validator.go b/binding/default_validator.go index ee69329ecc..87fc4c665b 100644 --- a/binding/default_validator.go +++ b/binding/default_validator.go @@ -20,15 +20,27 @@ type defaultValidator struct { type sliceValidateError []error +// Error concatenates all error elements in sliceValidateError into a single string separated by \n. func (err sliceValidateError) Error() string { - var errMsgs []string - for i, e := range err { - if e == nil { - continue + n := len(err) + switch n { + case 0: + return "" + default: + var b strings.Builder + if err[0] != nil { + fmt.Fprintf(&b, "[%d]: %s", 0, err[0].Error()) + } + if n > 1 { + for i := 1; i < n; i++ { + if err[i] != nil { + b.WriteString("\n") + fmt.Fprintf(&b, "[%d]: %s", i, err[i].Error()) + } + } } - errMsgs = append(errMsgs, fmt.Sprintf("[%d]: %s", i, e.Error())) + return b.String() } - return strings.Join(errMsgs, "\n") } var _ StructValidator = &defaultValidator{} diff --git a/binding/default_validator_benchmark_test.go b/binding/default_validator_benchmark_test.go new file mode 100644 index 0000000000..839cf710b5 --- /dev/null +++ b/binding/default_validator_benchmark_test.go @@ -0,0 +1,20 @@ +package binding + +import ( + "errors" + "strconv" + "testing" +) + +func BenchmarkSliceValidateError(b *testing.B) { + const size int = 100 + for i := 0; i < b.N; i++ { + e := make(sliceValidateError, size) + for j := 0; j < size; j++ { + e[j] = errors.New(strconv.Itoa(j)) + } + if len(e.Error()) == 0 { + b.Errorf("error") + } + } +} diff --git a/binding/default_validator_test.go b/binding/default_validator_test.go index e9c6de447d..e9debe59bf 100644 --- a/binding/default_validator_test.go +++ b/binding/default_validator_test.go @@ -16,6 +16,26 @@ func TestSliceValidateError(t *testing.T) { 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 two elements", + sliceValidateError{ + errors.New("first error"), + errors.New("second error"), + }, + "[0]: first error\n[1]: second error", + }, + {"has many elements", + sliceValidateError{ + errors.New("first error"), + errors.New("second error"), + nil, + nil, + nil, + errors.New("last error"), + }, + "[0]: first error\n[1]: second error\n[5]: last error", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 690aa2b1b9de7fe61dc15295210f64583dd3b90e Mon Sep 17 00:00:00 2001 From: voidman Date: Wed, 30 Jun 2021 00:53:56 +0800 Subject: [PATCH 008/346] feat(binding): support custom struct tag (#2720) * feat(binding): support custom struct tag Add function `binding.MapFormWithTag` (#2719) * doc: add 'bind form-data with custom struct tag' Add 'Bind form-data request with custom struct and custom tag' section (#2719) * test(binding): add test for MapFromWithTag --- README.md | 55 ++++++++++++++++++++++++++++++++++++ binding/form_mapping.go | 4 +++ binding/form_mapping_test.go | 9 ++++++ 3 files changed, 68 insertions(+) diff --git a/README.md b/README.md index ef37fd200d..ef80117935 100644 --- a/README.md +++ b/README.md @@ -2021,6 +2021,61 @@ enough to call binding at once. can be called by `c.ShouldBind()` multiple times without any damage to performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)). +### Bind form-data request with custom struct and custom tag + +```go +const ( + customerTag = "url" + defaultMemory = 32 << 20 +) + +type customerBinding struct {} + +func (customerBinding) Name() string { + return "form" +} + +func (customerBinding) Bind(req *http.Request, obj interface{}) error { + if err := req.ParseForm(); err != nil { + return err + } + if err := req.ParseMultipartForm(defaultMemory); err != nil { + if err != http.ErrNotMultipart { + return err + } + } + if err := binding.MapFormWithTag(obj, req.Form, customerTag); err != nil { + return err + } + return validate(obj) +} + +func validate(obj interface{}) error { + if binding.Validator == nil { + return nil + } + return binding.Validator.ValidateStruct(obj) +} + +// Now we can do this!!! +// FormA is a external type that we can't modify it's tag +type FormA struct { + FieldA string `url:"field_a"` +} + +func ListHandler(s *Service) func(ctx *gin.Context) { + return func(ctx *gin.Context) { + var urlBinding = customerBinding{} + var opt FormA + err := ctx.MustBindWith(&opt, urlBinding) + if err != nil { + ... + } + ... + } +} +``` + ### http2 server push http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information. diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 421c0f7124..cb66dd4a70 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -34,6 +34,10 @@ func mapForm(ptr interface{}, form map[string][]string) error { return mapFormByTag(ptr, form, "form") } +func MapFormWithTag(ptr interface{}, form map[string][]string, tag string) error { + return mapFormByTag(ptr, form, tag) +} + var emptyField = reflect.StructField{} func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error { diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 2675d46ba2..608594e28d 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -145,6 +145,15 @@ func TestMappingForm(t *testing.T) { assert.Equal(t, int(6), s.F) } +func TestMapFormWithTag(t *testing.T) { + var s struct { + F int `externalTag:"field"` + } + err := MapFormWithTag(&s, map[string][]string{"field": {"6"}}, "externalTag") + assert.NoError(t, err) + assert.Equal(t, int(6), s.F) +} + func TestMappingTime(t *testing.T) { var s struct { Time time.Time From 372cc4a0100efd3dbd80193b74ee65752d139ea8 Mon Sep 17 00:00:00 2001 From: Lanco <35420416+lancoLiu@users.noreply.github.com> Date: Fri, 2 Jul 2021 09:58:43 +0800 Subject: [PATCH 009/346] Fix typo (#2772) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index ede16fd4d8..8aabf7ae0f 100644 --- a/context.go +++ b/context.go @@ -732,7 +732,7 @@ 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, // the remote IP (coming form Request.RemoteAddr) is returned. func (c *Context) ClientIP() string { - // Check if we're running on a tursted platform + // Check if we're running on a trusted platform switch c.engine.TrustedPlatform { case PlatformGoogleAppEngine: if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { From 9c27053243cb24ecc90d01c8ff379bd98fed9c8e Mon Sep 17 00:00:00 2001 From: Lanco <35420416+lancoLiu@users.noreply.github.com> Date: Sun, 4 Jul 2021 10:37:13 +0800 Subject: [PATCH 010/346] byte alignment (#2774) --- gin.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/gin.go b/gin.go index fd4fa51241..7bdb68574b 100644 --- a/gin.go +++ b/gin.go @@ -99,6 +99,23 @@ type Engine struct { // `(*gin.Context).Request.RemoteAddr`. ForwardedByClientIP bool + // DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.GoogleAppEngine` INSTEAD + // #726 #755 If enabled, it will trust some headers starting with + // 'X-AppEngine...' for better integration with that PaaS. + AppEngine bool + + // If enabled, the url.RawPath will be used to find parameters. + UseRawPath bool + + // If true, the path value will be unescaped. + // If UseRawPath is false (by default), the UnescapePathValues effectively is true, + // as url.Path gonna be used, which is already unescaped. + UnescapePathValues bool + + // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes. + // See the PR #1817 and issue #1644 + RemoveExtraSlash bool + // List of headers used to obtain the client IP when // `(*gin.Engine).ForwardedByClientIP` is `true` and // `(*gin.Context).Request.RemoteAddr` is matched by at least one of the @@ -115,27 +132,10 @@ type Engine struct { // that platform, for example to determine the client IP TrustedPlatform string - // DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.GoogleAppEngine` INSTEAD - // #726 #755 If enabled, it will trust some headers starting with - // 'X-AppEngine...' for better integration with that PaaS. - AppEngine bool - - // If enabled, the url.RawPath will be used to find parameters. - UseRawPath bool - - // If true, the path value will be unescaped. - // If UseRawPath is false (by default), the UnescapePathValues effectively is true, - // as url.Path gonna be used, which is already unescaped. - UnescapePathValues bool - // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm // method call. MaxMultipartMemory int64 - // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes. - // See the PR #1817 and issue #1644 - RemoveExtraSlash bool - delims render.Delims secureJSONPrefix string HTMLRender render.HTMLRender From 9d2883ef47c726e9eb466cdd75123c0e4472ac65 Mon Sep 17 00:00:00 2001 From: Helios <674876158@qq.com> Date: Tue, 6 Jul 2021 16:36:32 +0800 Subject: [PATCH 011/346] update the version of validator in the comment (#2780) Co-authored-by: shangyilong --- binding/binding.go | 4 ++-- binding/binding_nomsgpack.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/binding/binding.go b/binding/binding.go index 5caeb581af..deb71661b8 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -49,7 +49,7 @@ type BindingUri interface { // StructValidator is the minimal interface which needs to be implemented in // order for it to be used as the validator engine for ensuring the correctness // of the request. Gin provides a default implementation for this using -// https://github.com/go-playground/validator/tree/v8.18.2. +// https://github.com/go-playground/validator/tree/v10.6.1. type StructValidator interface { // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right. // If the received type is a slice|array, the validation should be performed travel on every element. @@ -65,7 +65,7 @@ type StructValidator interface { } // Validator is the default validator which implements the StructValidator -// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2 +// interface. It uses https://github.com/go-playground/validator/tree/v10.6.1 // under the hood. var Validator StructValidator = &defaultValidator{} diff --git a/binding/binding_nomsgpack.go b/binding/binding_nomsgpack.go index 9afa3dcf6d..2342447070 100644 --- a/binding/binding_nomsgpack.go +++ b/binding/binding_nomsgpack.go @@ -47,7 +47,7 @@ type BindingUri interface { // StructValidator is the minimal interface which needs to be implemented in // order for it to be used as the validator engine for ensuring the correctness // of the request. Gin provides a default implementation for this using -// https://github.com/go-playground/validator/tree/v8.18.2. +// https://github.com/go-playground/validator/tree/v10.6.1. type StructValidator interface { // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right. // If the received type is not a struct, any validation should be skipped and nil must be returned. @@ -62,7 +62,7 @@ type StructValidator interface { } // Validator is the default validator which implements the StructValidator -// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2 +// interface. It uses https://github.com/go-playground/validator/tree/v10.6.1 // under the hood. var Validator StructValidator = &defaultValidator{} From c7a28f85320125709e47c592a92421a4d6f192a7 Mon Sep 17 00:00:00 2001 From: ziheng Date: Tue, 6 Jul 2021 16:37:14 +0800 Subject: [PATCH 012/346] use bit shift operation instead of division (#2776) --- context.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/context.go b/context.go index 8aabf7ae0f..ecf74ba9b1 100644 --- a/context.go +++ b/context.go @@ -40,7 +40,8 @@ const ( // BodyBytesKey indicates a default body bytes key. const BodyBytesKey = "_gin-gonic/gin/bodybyteskey" -const abortIndex int8 = math.MaxInt8 / 2 +// abortIndex represents a typical value used in abort functions. +const abortIndex int8 = math.MaxInt8 >> 1 // Context is the most important part of gin. It allows us to pass variables between middleware, // manage the flow, validate the JSON of a request and render a JSON response for example. From 3116a2d7a1bf5ed07c50a2b0851a4bed15b3e3b4 Mon Sep 17 00:00:00 2001 From: ziheng Date: Fri, 9 Jul 2021 10:30:44 +0800 Subject: [PATCH 013/346] use std http method constant instead of raw string (#2782) --- gin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 7bdb68574b..6ab2be66d0 100644 --- a/gin.go +++ b/gin.go @@ -539,7 +539,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) { c.writermem.WriteHeaderNow() return } - if httpMethod != "CONNECT" && rPath != "/" { + if httpMethod != http.MethodConnect && rPath != "/" { if value.tsr && engine.RedirectTrailingSlash { redirectTrailingSlash(c) return From f96678cb6b897f92921a2a52ae1c9f0248346a12 Mon Sep 17 00:00:00 2001 From: Lanco <35420416+lancoLiu@users.noreply.github.com> Date: Sun, 11 Jul 2021 14:38:45 +0800 Subject: [PATCH 014/346] use assert1 func (#2783) --- routergroup.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/routergroup.go b/routergroup.go index 6f14bf5935..bb24bd5232 100644 --- a/routergroup.go +++ b/routergroup.go @@ -214,9 +214,7 @@ func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileS func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain { finalSize := len(group.Handlers) + len(handlers) - if finalSize >= int(abortIndex) { - panic("too many handlers") - } + assert1(finalSize < int(abortIndex), "too many handlers") mergedHandlers := make(HandlersChain, finalSize) copy(mergedHandlers, group.Handlers) copy(mergedHandlers[len(group.Handlers):], handlers) From caf2802593277033683c4e8cb5f22c81cc35eae8 Mon Sep 17 00:00:00 2001 From: ziheng Date: Tue, 13 Jul 2021 09:44:19 +0800 Subject: [PATCH 015/346] Improve router group tests (#2787) --- routergroup_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/routergroup_test.go b/routergroup_test.go index 0e49d65b14..d6d8b45269 100644 --- a/routergroup_test.go +++ b/routergroup_test.go @@ -112,15 +112,19 @@ func TestRouterGroupInvalidStaticFile(t *testing.T) { } func TestRouterGroupTooManyHandlers(t *testing.T) { + const ( + panicValue = "too many handlers" + maximumCnt = abortIndex + ) router := New() - handlers1 := make([]HandlerFunc, 40) + handlers1 := make([]HandlerFunc, maximumCnt-1) router.Use(handlers1...) - handlers2 := make([]HandlerFunc, 26) - assert.Panics(t, func() { + handlers2 := make([]HandlerFunc, maximumCnt+1) + assert.PanicsWithValue(t, panicValue, func() { router.Use(handlers2...) }) - assert.Panics(t, func() { + assert.PanicsWithValue(t, panicValue, func() { router.GET("/", handlers2...) }) } From d4ca9a0fb1211108f0a893634ee01b0006890c16 Mon Sep 17 00:00:00 2001 From: qm012 <67568757+qm012@users.noreply.github.com> Date: Thu, 22 Jul 2021 17:58:15 -0500 Subject: [PATCH 016/346] fix #2762 (#2767) --- gin_integration_test.go | 78 ++++++++++++++++++++++++++++++++++-- tree.go | 88 +++++++++++++++++++++++++++++------------ tree_test.go | 36 +++++++++++++++++ 3 files changed, 172 insertions(+), 30 deletions(-) diff --git a/gin_integration_test.go b/gin_integration_test.go index 2eb2d52bf8..ed7196fd6e 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -22,7 +22,15 @@ import ( "github.com/stretchr/testify/assert" ) -func testRequest(t *testing.T, url string) { +// params[0]=url example:http://127.0.0.1:8080/index (cannot be empty) +// params[1]=response status (custom compare status) default:"200 OK" +// params[2]=response body (custom compare content) default:"it worked" +func testRequest(t *testing.T, params ...string) { + + if len(params) == 0 { + t.Fatal("url cannot be empty") + } + tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, @@ -30,14 +38,27 @@ func testRequest(t *testing.T, url string) { } client := &http.Client{Transport: tr} - resp, err := client.Get(url) + resp, err := client.Get(params[0]) assert.NoError(t, err) defer resp.Body.Close() body, ioerr := ioutil.ReadAll(resp.Body) assert.NoError(t, ioerr) - assert.Equal(t, "it worked", string(body), "resp body should match") - assert.Equal(t, "200 OK", resp.Status, "should get a 200") + + var responseStatus = "200 OK" + if len(params) > 1 && params[1] != "" { + responseStatus = params[1] + } + + var responseBody = "it worked" + if len(params) > 2 && params[2] != "" { + responseBody = params[2] + } + + assert.Equal(t, responseStatus, resp.Status, "should get a "+responseStatus) + if responseStatus == "200 OK" { + assert.Equal(t, responseBody, string(body), "resp body should match") + } } func TestRunEmpty(t *testing.T) { @@ -373,3 +394,52 @@ func testGetRequestHandler(t *testing.T, h http.Handler, url string) { assert.Equal(t, "it worked", w.Body.String(), "resp body should match") assert.Equal(t, 200, w.Code, "should get a 200") } + +func TestTreeRunDynamicRouting(t *testing.T) { + router := New() + router.GET("/aa/*xx", func(c *Context) { c.String(http.StatusOK, "/aa/*xx") }) + router.GET("/ab/*xx", func(c *Context) { c.String(http.StatusOK, "/ab/*xx") }) + router.GET("/", func(c *Context) { c.String(http.StatusOK, "home") }) + router.GET("/:cc", func(c *Context) { c.String(http.StatusOK, "/:cc") }) + router.GET("/:cc/cc", func(c *Context) { c.String(http.StatusOK, "/:cc/cc") }) + router.GET("/:cc/:dd/ee", func(c *Context) { c.String(http.StatusOK, "/:cc/:dd/ee") }) + router.GET("/:cc/:dd/:ee/ff", func(c *Context) { c.String(http.StatusOK, "/:cc/:dd/:ee/ff") }) + router.GET("/:cc/:dd/:ee/:ff/gg", func(c *Context) { c.String(http.StatusOK, "/:cc/:dd/:ee/:ff/gg") }) + router.GET("/:cc/:dd/:ee/:ff/:gg/hh", func(c *Context) { c.String(http.StatusOK, "/:cc/:dd/:ee/:ff/:gg/hh") }) + router.GET("/get/test/abc/", func(c *Context) { c.String(http.StatusOK, "/get/test/abc/") }) + router.GET("/get/:param/abc/", func(c *Context) { c.String(http.StatusOK, "/get/:param/abc/") }) + router.GET("/something/:paramname/thirdthing", func(c *Context) { c.String(http.StatusOK, "/something/:paramname/thirdthing") }) + router.GET("/something/secondthing/test", func(c *Context) { c.String(http.StatusOK, "/something/secondthing/test") }) + + ts := httptest.NewServer(router) + defer ts.Close() + + testRequest(t, ts.URL+"/", "", "home") + testRequest(t, ts.URL+"/aa/aa", "", "/aa/*xx") + testRequest(t, ts.URL+"/ab/ab", "", "/ab/*xx") + testRequest(t, ts.URL+"/all", "", "/:cc") + testRequest(t, ts.URL+"/all/cc", "", "/:cc/cc") + testRequest(t, ts.URL+"/a/cc", "", "/:cc/cc") + testRequest(t, ts.URL+"/c/d/ee", "", "/:cc/:dd/ee") + testRequest(t, ts.URL+"/c/d/e/ff", "", "/:cc/:dd/:ee/ff") + testRequest(t, ts.URL+"/c/d/e/f/gg", "", "/:cc/:dd/:ee/:ff/gg") + testRequest(t, ts.URL+"/c/d/e/f/g/hh", "", "/:cc/:dd/:ee/:ff/:gg/hh") + testRequest(t, ts.URL+"/a", "", "/:cc") + testRequest(t, ts.URL+"/get/test/abc/", "", "/get/test/abc/") + testRequest(t, ts.URL+"/get/te/abc/", "", "/get/:param/abc/") + testRequest(t, ts.URL+"/get/xx/abc/", "", "/get/:param/abc/") + testRequest(t, ts.URL+"/get/tt/abc/", "", "/get/:param/abc/") + testRequest(t, ts.URL+"/get/a/abc/", "", "/get/:param/abc/") + testRequest(t, ts.URL+"/get/t/abc/", "", "/get/:param/abc/") + testRequest(t, ts.URL+"/get/aa/abc/", "", "/get/:param/abc/") + testRequest(t, ts.URL+"/get/abas/abc/", "", "/get/:param/abc/") + testRequest(t, ts.URL+"/something/secondthing/test", "", "/something/secondthing/test") + testRequest(t, ts.URL+"/something/abcdad/thirdthing", "", "/something/:paramname/thirdthing") + testRequest(t, ts.URL+"/something/se/thirdthing", "", "/something/:paramname/thirdthing") + testRequest(t, ts.URL+"/something/s/thirdthing", "", "/something/:paramname/thirdthing") + testRequest(t, ts.URL+"/something/secondthing/thirdthing", "", "/something/:paramname/thirdthing") + // 404 not found + testRequest(t, ts.URL+"/a/dd", "404 Not Found") + testRequest(t, ts.URL+"/addr/dd/aa", "404 Not Found") + testRequest(t, ts.URL+"/something/secondthing/121", "404 Not Found") +} diff --git a/tree.go b/tree.go index 4b5656fb08..2e46b8e513 100644 --- a/tree.go +++ b/tree.go @@ -118,11 +118,6 @@ type node struct { fullPath string } -type skip struct { - path string - paramNode *node -} - // Increments priority of the given child and reorders if necessary func (n *node) incrementChildPrio(pos int) int { cs := n.children @@ -405,7 +400,23 @@ type nodeValue struct { // made if a handle exists with an extra (without the) trailing slash for the // given path. func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) { - var skipped *skip + // path: /abc/123/def + // level 1 router:abc + // level 2 router:123 + // level 3 router:def + var ( + skippedPath string + latestNode = n // not found `level 2 router` use latestNode + + // match '/' count + // matchNum < 1: `level 2 router` not found,the current node needs to be equal to latestNode + // matchNum >= 1: `level (2 or 3 or 4 or ...) router`: Normal handling + matchNum int // each match will accumulate + ) + //if path == "/", no need to look for tree node + if len(path) == 1 { + matchNum = 1 + } walk: // Outer loop for walking the tree for { @@ -418,32 +429,41 @@ walk: // Outer loop for walking the tree idxc := path[0] for i, c := range []byte(n.indices) { if c == idxc { - if strings.HasPrefix(n.children[len(n.children)-1].path, ":") { - skipped = &skip{ - path: prefix + path, - paramNode: &node{ - path: n.path, - wildChild: n.wildChild, - nType: n.nType, - priority: n.priority, - children: n.children, - handlers: n.handlers, - fullPath: n.fullPath, - }, + // strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild + if n.wildChild { + skippedPath = prefix + path + latestNode = &node{ + path: n.path, + wildChild: n.wildChild, + nType: n.nType, + priority: n.priority, + children: n.children, + handlers: n.handlers, + fullPath: n.fullPath, } } n = n.children[i] + + // match '/', If this condition is matched, the next route is found + if (len(n.fullPath) != 0 && n.wildChild) || path[len(path)-1] == '/' { + matchNum++ + } continue walk } } + // level 2 router not found,the current node needs to be equal to latestNode + if matchNum < 1 { + n = latestNode + } + // If there is no wildcard pattern, recommend a redirection if !n.wildChild { // Nothing found. // We can recommend to redirect to the same URL without a // trailing slash if a leaf exists for that path. - value.tsr = (path == "/" && n.handlers != nil) + value.tsr = path == "/" && n.handlers != nil return } @@ -483,6 +503,18 @@ walk: // Outer loop for walking the tree if len(n.children) > 0 { path = path[end:] n = n.children[0] + // next node,the latestNode needs to be equal to currentNode and handle next router + latestNode = n + // not found router in (level 1 router and handle next node),skippedPath cannot execute + // example: + // * /:cc/cc + // call /a/cc expectations:match/200 Actual:match/200 + // call /a/dd expectations:unmatch/404 Actual: panic + // call /addr/dd/aa expectations:unmatch/404 Actual: panic + // skippedPath: It can only be executed if the secondary route is not found + // matchNum: Go to the next level of routing tree node search,need add matchNum + skippedPath = "" + matchNum++ continue walk } @@ -535,6 +567,10 @@ walk: // Outer loop for walking the tree } if path == prefix { + // level 2 router not found and latestNode.wildChild is true + if matchNum < 1 && latestNode.wildChild { + n = latestNode.children[len(latestNode.children)-1] + } // We should have reached the node containing the handle. // Check if this node has a handle registered. if value.handlers = n.handlers; value.handlers != nil { @@ -564,18 +600,18 @@ walk: // Outer loop for walking the tree return } - if path != "/" && skipped != nil && strings.HasSuffix(skipped.path, path) { - path = skipped.path - n = skipped.paramNode - skipped = nil + // path != "/" && skippedPath != "" + if len(path) != 1 && len(skippedPath) > 0 && strings.HasSuffix(skippedPath, path) { + path = skippedPath + n = latestNode + skippedPath = "" continue walk } // Nothing found. We can recommend to redirect to the same URL with an // extra trailing slash if a leaf exists for that path - value.tsr = (path == "/") || - (len(prefix) == len(path)+1 && prefix[len(path)] == '/' && - path == prefix[:len(prefix)-1] && n.handlers != nil) + value.tsr = path == "/" || + (len(prefix) == len(path)+1 && n.handlers != nil) return } } diff --git a/tree_test.go b/tree_test.go index 7459317fa3..91213eee06 100644 --- a/tree_test.go +++ b/tree_test.go @@ -154,6 +154,18 @@ func TestTreeWildcard(t *testing.T) { "/info/:user/public", "/info/:user/project/:project", "/info/:user/project/golang", + "/aa/*xx", + "/ab/*xx", + "/:cc", + "/:cc/cc", + "/:cc/:dd/ee", + "/:cc/:dd/:ee/ff", + "/:cc/:dd/:ee/:ff/gg", + "/:cc/:dd/:ee/:ff/:gg/hh", + "/get/test/abc/", + "/get/:param/abc/", + "/something/:paramname/thirdthing", + "/something/secondthing/test", } for _, route := range routes { tree.addRoute(route, fakeHandler(route)) @@ -186,6 +198,30 @@ func TestTreeWildcard(t *testing.T) { {"/info/gordon/public", false, "/info/:user/public", Params{Param{Key: "user", Value: "gordon"}}}, {"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{Key: "user", Value: "gordon"}, Param{Key: "project", Value: "go"}}}, {"/info/gordon/project/golang", false, "/info/:user/project/golang", Params{Param{Key: "user", Value: "gordon"}}}, + {"/aa/aa", false, "/aa/*xx", Params{Param{Key: "xx", Value: "/aa"}}}, + {"/ab/ab", false, "/ab/*xx", Params{Param{Key: "xx", Value: "/ab"}}}, + {"/a", false, "/:cc", Params{Param{Key: "cc", Value: "a"}}}, + // * level 1 router match param will be Intercept first + // new PR handle (/all /all/cc /a/cc) + {"/all", false, "/:cc", Params{Param{Key: "cc", Value: "ll"}}}, + {"/all/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "ll"}}}, + {"/a/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: ""}}}, + {"/get/test/abc/", false, "/get/test/abc/", nil}, + {"/get/te/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "te"}}}, + {"/get/xx/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "xx"}}}, + {"/get/tt/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "tt"}}}, + {"/get/a/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "a"}}}, + {"/get/t/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "t"}}}, + {"/get/aa/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "aa"}}}, + {"/get/abas/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "abas"}}}, + {"/something/secondthing/test", false, "/something/secondthing/test", nil}, + {"/something/abcdad/thirdthing", false, "/something/:paramname/thirdthing", Params{Param{Key: "paramname", Value: "abcdad"}}}, + {"/something/se/thirdthing", false, "/something/:paramname/thirdthing", Params{Param{Key: "paramname", Value: "se"}}}, + {"/something/s/thirdthing", false, "/something/:paramname/thirdthing", Params{Param{Key: "paramname", Value: "s"}}}, + {"/c/d/ee", false, "/:cc/:dd/ee", Params{Param{Key: "cc", Value: "c"}, Param{Key: "dd", Value: "d"}}}, + {"/c/d/e/ff", false, "/:cc/:dd/:ee/ff", Params{Param{Key: "cc", Value: "c"}, Param{Key: "dd", Value: "d"}, Param{Key: "ee", Value: "e"}}}, + {"/c/d/e/f/gg", false, "/:cc/:dd/:ee/:ff/gg", Params{Param{Key: "cc", Value: "c"}, Param{Key: "dd", Value: "d"}, Param{Key: "ee", Value: "e"}, Param{Key: "ff", Value: "f"}}}, + {"/c/d/e/f/g/hh", false, "/:cc/:dd/:ee/:ff/:gg/hh", Params{Param{Key: "cc", Value: "c"}, Param{Key: "dd", Value: "d"}, Param{Key: "ee", Value: "e"}, Param{Key: "ff", Value: "f"}, Param{Key: "gg", Value: "g"}}}, }) checkPriorities(t, tree) From 0a55865c3fc4c2b30dce86dfa9cff7654ba9bc74 Mon Sep 17 00:00:00 2001 From: qm012 <67568757+qm012@users.noreply.github.com> Date: Sun, 25 Jul 2021 21:07:54 -0500 Subject: [PATCH 017/346] fix #2786 (#2796) * update match rule * add comments --- gin_integration_test.go | 82 +++++++++++++++++++++++++++++++++++++ tree.go | 67 +++++++++++++----------------- tree_test.go | 91 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 197 insertions(+), 43 deletions(-) diff --git a/gin_integration_test.go b/gin_integration_test.go index ed7196fd6e..094c46e871 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -410,6 +410,25 @@ func TestTreeRunDynamicRouting(t *testing.T) { router.GET("/get/:param/abc/", func(c *Context) { c.String(http.StatusOK, "/get/:param/abc/") }) router.GET("/something/:paramname/thirdthing", func(c *Context) { c.String(http.StatusOK, "/something/:paramname/thirdthing") }) router.GET("/something/secondthing/test", func(c *Context) { c.String(http.StatusOK, "/something/secondthing/test") }) + router.GET("/get/abc", func(c *Context) { c.String(http.StatusOK, "/get/abc") }) + router.GET("/get/:param", func(c *Context) { c.String(http.StatusOK, "/get/:param") }) + router.GET("/get/abc/123abc", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abc") }) + router.GET("/get/abc/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/:param") }) + router.GET("/get/abc/123abc/xxx8", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abc/xxx8") }) + router.GET("/get/abc/123abc/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abc/:param") }) + router.GET("/get/abc/123abc/xxx8/1234", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abc/xxx8/1234") }) + router.GET("/get/abc/123abc/xxx8/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abc/xxx8/:param") }) + router.GET("/get/abc/123abc/xxx8/1234/ffas", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abc/xxx8/1234/ffas") }) + router.GET("/get/abc/123abc/xxx8/1234/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abc/xxx8/1234/:param") }) + router.GET("/get/abc/123abc/xxx8/1234/kkdd/12c", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abc/xxx8/1234/kkdd/12c") }) + router.GET("/get/abc/123abc/xxx8/1234/kkdd/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abc/xxx8/1234/kkdd/:param") }) + router.GET("/get/abc/:param/test", func(c *Context) { c.String(http.StatusOK, "/get/abc/:param/test") }) + router.GET("/get/abc/123abd/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abd/:param") }) + router.GET("/get/abc/123abddd/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abddd/:param") }) + router.GET("/get/abc/123/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123/:param") }) + router.GET("/get/abc/123abg/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abg/:param") }) + router.GET("/get/abc/123abf/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abf/:param") }) + router.GET("/get/abc/123abfff/:param", func(c *Context) { c.String(http.StatusOK, "/get/abc/123abfff/:param") }) ts := httptest.NewServer(router) defer ts.Close() @@ -424,8 +443,26 @@ func TestTreeRunDynamicRouting(t *testing.T) { testRequest(t, ts.URL+"/c/d/e/ff", "", "/:cc/:dd/:ee/ff") testRequest(t, ts.URL+"/c/d/e/f/gg", "", "/:cc/:dd/:ee/:ff/gg") testRequest(t, ts.URL+"/c/d/e/f/g/hh", "", "/:cc/:dd/:ee/:ff/:gg/hh") + testRequest(t, ts.URL+"/cc/dd/ee/ff/gg/hh", "", "/:cc/:dd/:ee/:ff/:gg/hh") testRequest(t, ts.URL+"/a", "", "/:cc") + testRequest(t, ts.URL+"/d", "", "/:cc") + testRequest(t, ts.URL+"/ad", "", "/:cc") + testRequest(t, ts.URL+"/dd", "", "/:cc") + testRequest(t, ts.URL+"/aa", "", "/:cc") + testRequest(t, ts.URL+"/aaa", "", "/:cc") + testRequest(t, ts.URL+"/aaa/cc", "", "/:cc/cc") + testRequest(t, ts.URL+"/ab", "", "/:cc") + testRequest(t, ts.URL+"/abb", "", "/:cc") + testRequest(t, ts.URL+"/abb/cc", "", "/:cc/cc") + testRequest(t, ts.URL+"/dddaa", "", "/:cc") + testRequest(t, ts.URL+"/allxxxx", "", "/:cc") + testRequest(t, ts.URL+"/alldd", "", "/:cc") + testRequest(t, ts.URL+"/cc/cc", "", "/:cc/cc") + testRequest(t, ts.URL+"/ccc/cc", "", "/:cc/cc") + testRequest(t, ts.URL+"/deedwjfs/cc", "", "/:cc/cc") + testRequest(t, ts.URL+"/acllcc/cc", "", "/:cc/cc") testRequest(t, ts.URL+"/get/test/abc/", "", "/get/test/abc/") + testRequest(t, ts.URL+"/get/testaa/abc/", "", "/get/:param/abc/") testRequest(t, ts.URL+"/get/te/abc/", "", "/get/:param/abc/") testRequest(t, ts.URL+"/get/xx/abc/", "", "/get/:param/abc/") testRequest(t, ts.URL+"/get/tt/abc/", "", "/get/:param/abc/") @@ -434,10 +471,55 @@ func TestTreeRunDynamicRouting(t *testing.T) { testRequest(t, ts.URL+"/get/aa/abc/", "", "/get/:param/abc/") testRequest(t, ts.URL+"/get/abas/abc/", "", "/get/:param/abc/") testRequest(t, ts.URL+"/something/secondthing/test", "", "/something/secondthing/test") + testRequest(t, ts.URL+"/something/secondthingaaaa/thirdthing", "", "/something/:paramname/thirdthing") testRequest(t, ts.URL+"/something/abcdad/thirdthing", "", "/something/:paramname/thirdthing") testRequest(t, ts.URL+"/something/se/thirdthing", "", "/something/:paramname/thirdthing") testRequest(t, ts.URL+"/something/s/thirdthing", "", "/something/:paramname/thirdthing") testRequest(t, ts.URL+"/something/secondthing/thirdthing", "", "/something/:paramname/thirdthing") + testRequest(t, ts.URL+"/get/abc", "", "/get/abc") + testRequest(t, ts.URL+"/get/a", "", "/get/:param") + testRequest(t, ts.URL+"/get/abz", "", "/get/:param") + testRequest(t, ts.URL+"/get/12a", "", "/get/:param") + testRequest(t, ts.URL+"/get/abcd", "", "/get/:param") + testRequest(t, ts.URL+"/get/abc/123abc", "", "/get/abc/123abc") + testRequest(t, ts.URL+"/get/abc/12", "", "/get/abc/:param") + testRequest(t, ts.URL+"/get/abc/123ab", "", "/get/abc/:param") + testRequest(t, ts.URL+"/get/abc/xyz", "", "/get/abc/:param") + testRequest(t, ts.URL+"/get/abc/123abcddxx", "", "/get/abc/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8", "", "/get/abc/123abc/xxx8") + testRequest(t, ts.URL+"/get/abc/123abc/x", "", "/get/abc/123abc/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx", "", "/get/abc/123abc/:param") + testRequest(t, ts.URL+"/get/abc/123abc/abc", "", "/get/abc/123abc/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8xxas", "", "/get/abc/123abc/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234", "", "/get/abc/123abc/xxx8/1234") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1", "", "/get/abc/123abc/xxx8/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/123", "", "/get/abc/123abc/xxx8/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/78k", "", "/get/abc/123abc/xxx8/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234xxxd", "", "/get/abc/123abc/xxx8/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/ffas", "", "/get/abc/123abc/xxx8/1234/ffas") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/f", "", "/get/abc/123abc/xxx8/1234/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/ffa", "", "/get/abc/123abc/xxx8/1234/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/kka", "", "/get/abc/123abc/xxx8/1234/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/ffas321", "", "/get/abc/123abc/xxx8/1234/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/kkdd/12c", "", "/get/abc/123abc/xxx8/1234/kkdd/12c") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/kkdd/1", "", "/get/abc/123abc/xxx8/1234/kkdd/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/kkdd/12", "", "/get/abc/123abc/xxx8/1234/kkdd/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/kkdd/12b", "", "/get/abc/123abc/xxx8/1234/kkdd/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/kkdd/34", "", "/get/abc/123abc/xxx8/1234/kkdd/:param") + testRequest(t, ts.URL+"/get/abc/123abc/xxx8/1234/kkdd/12c2e3", "", "/get/abc/123abc/xxx8/1234/kkdd/:param") + testRequest(t, ts.URL+"/get/abc/12/test", "", "/get/abc/:param/test") + testRequest(t, ts.URL+"/get/abc/123abdd/test", "", "/get/abc/:param/test") + testRequest(t, ts.URL+"/get/abc/123abdddf/test", "", "/get/abc/:param/test") + testRequest(t, ts.URL+"/get/abc/123ab/test", "", "/get/abc/:param/test") + testRequest(t, ts.URL+"/get/abc/123abgg/test", "", "/get/abc/:param/test") + testRequest(t, ts.URL+"/get/abc/123abff/test", "", "/get/abc/:param/test") + testRequest(t, ts.URL+"/get/abc/123abffff/test", "", "/get/abc/:param/test") + testRequest(t, ts.URL+"/get/abc/123abd/test", "", "/get/abc/123abd/:param") + testRequest(t, ts.URL+"/get/abc/123abddd/test", "", "/get/abc/123abddd/:param") + testRequest(t, ts.URL+"/get/abc/123/test22", "", "/get/abc/123/:param") + testRequest(t, ts.URL+"/get/abc/123abg/test", "", "/get/abc/123abg/:param") + testRequest(t, ts.URL+"/get/abc/123abf/testss", "", "/get/abc/123abf/:param") + testRequest(t, ts.URL+"/get/abc/123abfff/te", "", "/get/abc/123abfff/:param") // 404 not found testRequest(t, ts.URL+"/a/dd", "404 Not Found") testRequest(t, ts.URL+"/addr/dd/aa", "404 Not Found") diff --git a/tree.go b/tree.go index 2e46b8e513..eb549591d6 100644 --- a/tree.go +++ b/tree.go @@ -400,23 +400,10 @@ type nodeValue struct { // made if a handle exists with an extra (without the) trailing slash for the // given path. func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) { - // path: /abc/123/def - // level 1 router:abc - // level 2 router:123 - // level 3 router:def var ( skippedPath string - latestNode = n // not found `level 2 router` use latestNode - - // match '/' count - // matchNum < 1: `level 2 router` not found,the current node needs to be equal to latestNode - // matchNum >= 1: `level (2 or 3 or 4 or ...) router`: Normal handling - matchNum int // each match will accumulate + latestNode = n // Caching the latest node ) - //if path == "/", no need to look for tree node - if len(path) == 1 { - matchNum = 1 - } walk: // Outer loop for walking the tree for { @@ -444,17 +431,13 @@ walk: // Outer loop for walking the tree } n = n.children[i] - - // match '/', If this condition is matched, the next route is found - if (len(n.fullPath) != 0 && n.wildChild) || path[len(path)-1] == '/' { - matchNum++ - } continue walk } } - - // level 2 router not found,the current node needs to be equal to latestNode - if matchNum < 1 { + // If the path at the end of the loop is not equal to '/' and the current node has no child nodes + // the current node needs to be equal to the latest matching node + matched := path != "/" && !n.wildChild + if matched { n = latestNode } @@ -472,6 +455,16 @@ walk: // Outer loop for walking the tree switch n.nType { case param: + // fix truncate the parameter + // tree_test.go line: 204 + if matched { + path = prefix + path + // The saved path is used after the prefix route is intercepted by matching + if n.indices == "/" { + path = skippedPath[1:] + } + } + // Find param end (either '/' or path end) end := 0 for end < len(path) && path[end] != '/' { @@ -503,18 +496,6 @@ walk: // Outer loop for walking the tree if len(n.children) > 0 { path = path[end:] n = n.children[0] - // next node,the latestNode needs to be equal to currentNode and handle next router - latestNode = n - // not found router in (level 1 router and handle next node),skippedPath cannot execute - // example: - // * /:cc/cc - // call /a/cc expectations:match/200 Actual:match/200 - // call /a/dd expectations:unmatch/404 Actual: panic - // call /addr/dd/aa expectations:unmatch/404 Actual: panic - // skippedPath: It can only be executed if the secondary route is not found - // matchNum: Go to the next level of routing tree node search,need add matchNum - skippedPath = "" - matchNum++ continue walk } @@ -567,8 +548,9 @@ walk: // Outer loop for walking the tree } if path == prefix { - // level 2 router not found and latestNode.wildChild is true - if matchNum < 1 && latestNode.wildChild { + // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node + // the current node needs to be equal to the latest matching node + if latestNode.wildChild && n.handlers == nil && path != "/" { n = latestNode.children[len(latestNode.children)-1] } // We should have reached the node containing the handle. @@ -600,10 +582,17 @@ walk: // Outer loop for walking the tree return } - // path != "/" && skippedPath != "" - if len(path) != 1 && len(skippedPath) > 0 && strings.HasSuffix(skippedPath, path) { + if path != "/" && len(skippedPath) > 0 && strings.HasSuffix(skippedPath, path) { path = skippedPath - n = latestNode + // Reduce the number of cycles + n, latestNode = latestNode, n + // skippedPath cannot execute + // example: + // * /:cc/cc + // call /a/cc expectations:match/200 Actual:match/200 + // call /a/dd expectations:unmatch/404 Actual: panic + // call /addr/dd/aa expectations:unmatch/404 Actual: panic + // skippedPath: It can only be executed if the secondary route is not found skippedPath = "" continue walk } diff --git a/tree_test.go b/tree_test.go index 91213eee06..ea13c30ee9 100644 --- a/tree_test.go +++ b/tree_test.go @@ -166,6 +166,25 @@ func TestTreeWildcard(t *testing.T) { "/get/:param/abc/", "/something/:paramname/thirdthing", "/something/secondthing/test", + "/get/abc", + "/get/:param", + "/get/abc/123abc", + "/get/abc/:param", + "/get/abc/123abc/xxx8", + "/get/abc/123abc/:param", + "/get/abc/123abc/xxx8/1234", + "/get/abc/123abc/xxx8/:param", + "/get/abc/123abc/xxx8/1234/ffas", + "/get/abc/123abc/xxx8/1234/:param", + "/get/abc/123abc/xxx8/1234/kkdd/12c", + "/get/abc/123abc/xxx8/1234/kkdd/:param", + "/get/abc/:param/test", + "/get/abc/123abd/:param", + "/get/abc/123abddd/:param", + "/get/abc/123/:param", + "/get/abc/123abg/:param", + "/get/abc/123abf/:param", + "/get/abc/123abfff/:param", } for _, route := range routes { tree.addRoute(route, fakeHandler(route)) @@ -201,13 +220,31 @@ func TestTreeWildcard(t *testing.T) { {"/aa/aa", false, "/aa/*xx", Params{Param{Key: "xx", Value: "/aa"}}}, {"/ab/ab", false, "/ab/*xx", Params{Param{Key: "xx", Value: "/ab"}}}, {"/a", false, "/:cc", Params{Param{Key: "cc", Value: "a"}}}, - // * level 1 router match param will be Intercept first + // * Error with argument being intercepted // new PR handle (/all /all/cc /a/cc) - {"/all", false, "/:cc", Params{Param{Key: "cc", Value: "ll"}}}, - {"/all/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "ll"}}}, - {"/a/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: ""}}}, + // fix PR: https://github.com/gin-gonic/gin/pull/2796 + {"/all", false, "/:cc", Params{Param{Key: "cc", Value: "all"}}}, + {"/d", false, "/:cc", Params{Param{Key: "cc", Value: "d"}}}, + {"/ad", false, "/:cc", Params{Param{Key: "cc", Value: "ad"}}}, + {"/dd", false, "/:cc", Params{Param{Key: "cc", Value: "dd"}}}, + {"/dddaa", false, "/:cc", Params{Param{Key: "cc", Value: "dddaa"}}}, + {"/aa", false, "/:cc", Params{Param{Key: "cc", Value: "aa"}}}, + {"/aaa", false, "/:cc", Params{Param{Key: "cc", Value: "aaa"}}}, + {"/aaa/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "aaa"}}}, + {"/ab", false, "/:cc", Params{Param{Key: "cc", Value: "ab"}}}, + {"/abb", false, "/:cc", Params{Param{Key: "cc", Value: "abb"}}}, + {"/abb/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "abb"}}}, + {"/allxxxx", false, "/:cc", Params{Param{Key: "cc", Value: "allxxxx"}}}, + {"/alldd", false, "/:cc", Params{Param{Key: "cc", Value: "alldd"}}}, + {"/all/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "all"}}}, + {"/a/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "a"}}}, + {"/cc/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "cc"}}}, + {"/ccc/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "ccc"}}}, + {"/deedwjfs/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "deedwjfs"}}}, + {"/acllcc/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "acllcc"}}}, {"/get/test/abc/", false, "/get/test/abc/", nil}, {"/get/te/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "te"}}}, + {"/get/testaa/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "testaa"}}}, {"/get/xx/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "xx"}}}, {"/get/tt/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "tt"}}}, {"/get/a/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "a"}}}, @@ -216,12 +253,58 @@ func TestTreeWildcard(t *testing.T) { {"/get/abas/abc/", false, "/get/:param/abc/", Params{Param{Key: "param", Value: "abas"}}}, {"/something/secondthing/test", false, "/something/secondthing/test", nil}, {"/something/abcdad/thirdthing", false, "/something/:paramname/thirdthing", Params{Param{Key: "paramname", Value: "abcdad"}}}, + {"/something/secondthingaaaa/thirdthing", false, "/something/:paramname/thirdthing", Params{Param{Key: "paramname", Value: "secondthingaaaa"}}}, {"/something/se/thirdthing", false, "/something/:paramname/thirdthing", Params{Param{Key: "paramname", Value: "se"}}}, {"/something/s/thirdthing", false, "/something/:paramname/thirdthing", Params{Param{Key: "paramname", Value: "s"}}}, {"/c/d/ee", false, "/:cc/:dd/ee", Params{Param{Key: "cc", Value: "c"}, Param{Key: "dd", Value: "d"}}}, {"/c/d/e/ff", false, "/:cc/:dd/:ee/ff", Params{Param{Key: "cc", Value: "c"}, Param{Key: "dd", Value: "d"}, Param{Key: "ee", Value: "e"}}}, {"/c/d/e/f/gg", false, "/:cc/:dd/:ee/:ff/gg", Params{Param{Key: "cc", Value: "c"}, Param{Key: "dd", Value: "d"}, Param{Key: "ee", Value: "e"}, Param{Key: "ff", Value: "f"}}}, {"/c/d/e/f/g/hh", false, "/:cc/:dd/:ee/:ff/:gg/hh", Params{Param{Key: "cc", Value: "c"}, Param{Key: "dd", Value: "d"}, Param{Key: "ee", Value: "e"}, Param{Key: "ff", Value: "f"}, Param{Key: "gg", Value: "g"}}}, + {"/cc/dd/ee/ff/gg/hh", false, "/:cc/:dd/:ee/:ff/:gg/hh", Params{Param{Key: "cc", Value: "cc"}, Param{Key: "dd", Value: "dd"}, Param{Key: "ee", Value: "ee"}, Param{Key: "ff", Value: "ff"}, Param{Key: "gg", Value: "gg"}}}, + {"/get/abc", false, "/get/abc", nil}, + {"/get/a", false, "/get/:param", Params{Param{Key: "param", Value: "a"}}}, + {"/get/abz", false, "/get/:param", Params{Param{Key: "param", Value: "abz"}}}, + {"/get/12a", false, "/get/:param", Params{Param{Key: "param", Value: "12a"}}}, + {"/get/abcd", false, "/get/:param", Params{Param{Key: "param", Value: "abcd"}}}, + {"/get/abc/123abc", false, "/get/abc/123abc", nil}, + {"/get/abc/12", false, "/get/abc/:param", Params{Param{Key: "param", Value: "12"}}}, + {"/get/abc/123ab", false, "/get/abc/:param", Params{Param{Key: "param", Value: "123ab"}}}, + {"/get/abc/xyz", false, "/get/abc/:param", Params{Param{Key: "param", Value: "xyz"}}}, + {"/get/abc/123abcddxx", false, "/get/abc/:param", Params{Param{Key: "param", Value: "123abcddxx"}}}, + {"/get/abc/123abc/xxx8", false, "/get/abc/123abc/xxx8", nil}, + {"/get/abc/123abc/x", false, "/get/abc/123abc/:param", Params{Param{Key: "param", Value: "x"}}}, + {"/get/abc/123abc/xxx", false, "/get/abc/123abc/:param", Params{Param{Key: "param", Value: "xxx"}}}, + {"/get/abc/123abc/abc", false, "/get/abc/123abc/:param", Params{Param{Key: "param", Value: "abc"}}}, + {"/get/abc/123abc/xxx8xxas", false, "/get/abc/123abc/:param", Params{Param{Key: "param", Value: "xxx8xxas"}}}, + {"/get/abc/123abc/xxx8/1234", false, "/get/abc/123abc/xxx8/1234", nil}, + {"/get/abc/123abc/xxx8/1", false, "/get/abc/123abc/xxx8/:param", Params{Param{Key: "param", Value: "1"}}}, + {"/get/abc/123abc/xxx8/123", false, "/get/abc/123abc/xxx8/:param", Params{Param{Key: "param", Value: "123"}}}, + {"/get/abc/123abc/xxx8/78k", false, "/get/abc/123abc/xxx8/:param", Params{Param{Key: "param", Value: "78k"}}}, + {"/get/abc/123abc/xxx8/1234xxxd", false, "/get/abc/123abc/xxx8/:param", Params{Param{Key: "param", Value: "1234xxxd"}}}, + {"/get/abc/123abc/xxx8/1234/ffas", false, "/get/abc/123abc/xxx8/1234/ffas", nil}, + {"/get/abc/123abc/xxx8/1234/f", false, "/get/abc/123abc/xxx8/1234/:param", Params{Param{Key: "param", Value: "f"}}}, + {"/get/abc/123abc/xxx8/1234/ffa", false, "/get/abc/123abc/xxx8/1234/:param", Params{Param{Key: "param", Value: "ffa"}}}, + {"/get/abc/123abc/xxx8/1234/kka", false, "/get/abc/123abc/xxx8/1234/:param", Params{Param{Key: "param", Value: "kka"}}}, + {"/get/abc/123abc/xxx8/1234/ffas321", false, "/get/abc/123abc/xxx8/1234/:param", Params{Param{Key: "param", Value: "ffas321"}}}, + {"/get/abc/123abc/xxx8/1234/kkdd/12c", false, "/get/abc/123abc/xxx8/1234/kkdd/12c", nil}, + {"/get/abc/123abc/xxx8/1234/kkdd/1", false, "/get/abc/123abc/xxx8/1234/kkdd/:param", Params{Param{Key: "param", Value: "1"}}}, + {"/get/abc/123abc/xxx8/1234/kkdd/12", false, "/get/abc/123abc/xxx8/1234/kkdd/:param", Params{Param{Key: "param", Value: "12"}}}, + {"/get/abc/123abc/xxx8/1234/kkdd/12b", false, "/get/abc/123abc/xxx8/1234/kkdd/:param", Params{Param{Key: "param", Value: "12b"}}}, + {"/get/abc/123abc/xxx8/1234/kkdd/34", false, "/get/abc/123abc/xxx8/1234/kkdd/:param", Params{Param{Key: "param", Value: "34"}}}, + {"/get/abc/123abc/xxx8/1234/kkdd/12c2e3", false, "/get/abc/123abc/xxx8/1234/kkdd/:param", Params{Param{Key: "param", Value: "12c2e3"}}}, + {"/get/abc/12/test", false, "/get/abc/:param/test", Params{Param{Key: "param", Value: "12"}}}, + {"/get/abc/123abdd/test", false, "/get/abc/:param/test", Params{Param{Key: "param", Value: "123abdd"}}}, + {"/get/abc/123abdddf/test", false, "/get/abc/:param/test", Params{Param{Key: "param", Value: "123abdddf"}}}, + {"/get/abc/123ab/test", false, "/get/abc/:param/test", Params{Param{Key: "param", Value: "123ab"}}}, + {"/get/abc/123abgg/test", false, "/get/abc/:param/test", Params{Param{Key: "param", Value: "123abgg"}}}, + {"/get/abc/123abff/test", false, "/get/abc/:param/test", Params{Param{Key: "param", Value: "123abff"}}}, + {"/get/abc/123abffff/test", false, "/get/abc/:param/test", Params{Param{Key: "param", Value: "123abffff"}}}, + {"/get/abc/123abd/test", false, "/get/abc/123abd/:param", Params{Param{Key: "param", Value: "test"}}}, + {"/get/abc/123abddd/test", false, "/get/abc/123abddd/:param", Params{Param{Key: "param", Value: "test"}}}, + {"/get/abc/123/test22", false, "/get/abc/123/:param", Params{Param{Key: "param", Value: "test22"}}}, + {"/get/abc/123abg/test", false, "/get/abc/123abg/:param", Params{Param{Key: "param", Value: "test"}}}, + {"/get/abc/123abf/testss", false, "/get/abc/123abf/:param", Params{Param{Key: "param", Value: "testss"}}}, + {"/get/abc/123abfff/te", false, "/get/abc/123abfff/:param", Params{Param{Key: "param", Value: "te"}}}, }) checkPriorities(t, tree) From 11aa11a65618b6b66bf2b5123e08db6e879c4a1e Mon Sep 17 00:00:00 2001 From: "Eren A. Akyol" Date: Tue, 27 Jul 2021 02:59:53 +0300 Subject: [PATCH 018/346] fix readability in recovery test (#2797) --- recovery_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/recovery_test.go b/recovery_test.go index 6cc2a47af4..d164bfa369 100644 --- a/recovery_test.go +++ b/recovery_test.go @@ -92,14 +92,14 @@ func TestPanicWithAbort(t *testing.T) { func TestSource(t *testing.T) { bs := source(nil, 0) - assert.Equal(t, []byte("???"), bs) + assert.Equal(t, dunno, bs) in := [][]byte{ []byte("Hello world."), []byte("Hi, gin.."), } bs = source(in, 10) - assert.Equal(t, []byte("???"), bs) + assert.Equal(t, dunno, bs) bs = source(in, 1) assert.Equal(t, []byte("Hello world."), bs) @@ -107,7 +107,7 @@ func TestSource(t *testing.T) { func TestFunction(t *testing.T) { bs := function(1) - assert.Equal(t, []byte("???"), bs) + assert.Equal(t, dunno, bs) } // TestPanicWithBrokenPipe asserts that recovery specifically handles From 9a575a4c05601954e0848ff8271238dd73485536 Mon Sep 17 00:00:00 2001 From: wei Date: Sun, 1 Aug 2021 00:46:53 +0800 Subject: [PATCH 019/346] fallback Context.Deadline() Context.Done() Context.Err() to Context.Request.Context() (#2769) * fallback Context.Deadline() Context.Done() Context.Err() to Context.Request.Context() * update comments wording --- context.go | 24 +++++++++++++++--------- context_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/context.go b/context.go index ecf74ba9b1..1a953006c0 100644 --- a/context.go +++ b/context.go @@ -1161,22 +1161,28 @@ func (c *Context) SetAccepted(formats ...string) { /***** GOLANG.ORG/X/NET/CONTEXT *****/ /************************************/ -// Deadline always returns that there is no deadline (ok==false), -// maybe you want to use Request.Context().Deadline() instead. +// Deadline returns that there is no deadline (ok==false) when c.Request has no Context. func (c *Context) Deadline() (deadline time.Time, ok bool) { - return + if c.Request == nil || c.Request.Context() == nil { + return + } + return c.Request.Context().Deadline() } -// Done always returns nil (chan which will wait forever), -// if you want to abort your work when the connection was closed -// you should use Request.Context().Done() instead. +// Done returns nil (chan which will wait forever) when c.Request has no Context. func (c *Context) Done() <-chan struct{} { - return nil + if c.Request == nil || c.Request.Context() == nil { + return nil + } + return c.Request.Context().Done() } -// Err always returns nil, maybe you want to use Request.Context().Err() instead. +// Err returns nil when c.Request has no Context. func (c *Context) Err() error { - return nil + if c.Request == nil || c.Request.Context() == nil { + return nil + } + return c.Request.Context().Err() } // Value returns the value associated with this context for key, or nil diff --git a/context_test.go b/context_test.go index 2a4d21856e..ffbe5ccaa3 100644 --- a/context_test.go +++ b/context_test.go @@ -2058,6 +2058,48 @@ func TestRemoteIPFail(t *testing.T) { assert.False(t, trust) } +func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) { + c := &Context{} + deadline, ok := c.Deadline() + assert.Zero(t, deadline) + assert.False(t, ok) + + c2 := &Context{} + c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil) + d := time.Now().Add(time.Second) + ctx, cancel := context.WithDeadline(context.Background(), d) + defer cancel() + c2.Request = c2.Request.WithContext(ctx) + deadline, ok = c2.Deadline() + assert.Equal(t, d, deadline) + assert.True(t, ok) +} + +func TestContextWithFallbackDoneFromRequestContext(t *testing.T) { + c := &Context{} + assert.Nil(t, c.Done()) + + c2 := &Context{} + c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil) + ctx, cancel := context.WithCancel(context.Background()) + c2.Request = c2.Request.WithContext(ctx) + cancel() + assert.NotNil(t, <-c2.Done()) +} + +func TestContextWithFallbackErrFromRequestContext(t *testing.T) { + c := &Context{} + assert.Nil(t, c.Err()) + + c2 := &Context{} + c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil) + ctx, cancel := context.WithCancel(context.Background()) + c2.Request = c2.Request.WithContext(ctx) + cancel() + + assert.EqualError(t, c2.Err(), context.Canceled.Error()) +} + func TestContextWithFallbackValueFromRequestContext(t *testing.T) { tests := []struct { name string From 6ebb945bd7ac10b0fec87299129b6d9603e1e4ab Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 3 Aug 2021 10:26:26 +0800 Subject: [PATCH 020/346] docs: release v1.7.3 (#2802) * docs: release v1.7.3 Signed-off-by: Bo-Yi Wu * fix: format Signed-off-by: Bo-Yi Wu --- CHANGELOG.md | 6 ++++++ version.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a28edc840a..308af74c36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Gin ChangeLog +## Gin v1.7.3 + +### BUGFIXES + +* fix level 1 router match [#2767](https://github.com/gin-gonic/gin/issues/2767), [#2796](https://github.com/gin-gonic/gin/issues/2796) + ## Gin v1.7.2 ### BUGFIXES diff --git a/version.go b/version.go index a80ab69a8f..535bfc8273 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.2" +const Version = "v1.7.3" From b463b1c2a175cd17365d114e4827aa2d680c3dc9 Mon Sep 17 00:00:00 2001 From: goqihoo Date: Wed, 11 Aug 2021 09:42:25 +0800 Subject: [PATCH 021/346] Update README.md (#2804) 1. c.FullPath() == "/user/:name/*action" get following error: evaluated but not used 2. c.String(http.StatusOK, "The available groups are [...]", name) get following error: undefined: name --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ef80117935..198bf01193 100644 --- a/README.md +++ b/README.md @@ -256,14 +256,15 @@ func main() { // For each matched request Context will hold the route definition router.POST("/user/:name/*action", func(c *gin.Context) { - c.FullPath() == "/user/:name/*action" // true + b := c.FullPath() == "/user/:name/*action" // true + c.String(http.StatusOK, "%t", b) }) // This handler will add a new router for /user/groups. // Exact routes are resolved before param routes, regardless of the order they were defined. // Routes starting with /user/groups are never interpreted as /user/:name/... routes router.GET("/user/groups", func(c *gin.Context) { - c.String(http.StatusOK, "The available groups are [...]", name) + c.String(http.StatusOK, "The available groups are [...]") }) router.Run(":8080") From 435a76b735db28b6223b323d30503de51c637b89 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Thu, 19 Aug 2021 09:46:31 +0200 Subject: [PATCH 022/346] chore(ci): update dependencies (#2827) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Matthieu MOREL --- .github/dependabot.yml | 10 ++ .github/workflows/gin.yml | 41 +++-- .golangci.yml | 23 +++ binding/binding_test.go | 3 +- binding/form_mapping.go | 5 +- binding/form_mapping_test.go | 2 +- binding/protobuf.go | 2 +- binding/uri.go | 2 +- context_test.go | 24 ++- go.mod | 14 +- go.sum | 90 ++++++---- mode.go | 6 +- render/json.go | 4 +- render/protobuf.go | 2 +- render/render_test.go | 11 +- response_writer_test.go | 14 +- testdata/protoexample/test.pb.go | 299 +++++++++++++++++++++++++------ tree_test.go | 2 +- 18 files changed, 411 insertions(+), 143 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .golangci.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..632e8eb25c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + - package-ecosystem: gomod + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 85cc560ed7..ab450ab834 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -9,12 +9,31 @@ on: - master jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Setup go + uses: actions/setup-go@v2 + with: + go-version: '^1.16' + - name: Checkout repository + uses: actions/checkout@v2 + - name: Setup golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + version: v1.41.1 + args: --verbose test: strategy: matrix: os: [ubuntu-latest, macos-latest] go: [1.13, 1.14, 1.15, 1.16] test-tags: ['', nomsgpack] + include: + - os: ubuntu-latest + go-build: ~/.cache/go-build + - os: macos-latest + go-build: ~/Library/Caches/go-build name: ${{ matrix.os }} @ Go ${{ matrix.go }} ${{ matrix.test-tags }} runs-on: ${{ matrix.os }} env: @@ -31,21 +50,23 @@ jobs: uses: actions/checkout@v2 with: ref: ${{ github.ref }} - - - name: Install Dependencies - run: make tools - - - name: Run Check - run: | - make vet - make fmt-check - make misspell-check + + - uses: actions/cache@v2 + with: + path: | + ${{ matrix.go-build }} + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- - name: Run Tests run: make test - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 + with: + flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }} notification-gitter: needs: test runs-on: ubuntu-latest diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000000..1c3cb758e0 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,23 @@ +linters: + enable: + - gofmt + - misspell + - revive +issues: + exclude-rules: + - linters: + - deadcode + text: "`static` is unused" + - linters: + - structcheck + - unused + text: "`data` is unused" + - linters: + - staticcheck + text: "SA1019:" + - linters: + - revive + text: "var-naming:" + - linters: + - revive + text: "exported:" diff --git a/binding/binding_test.go b/binding/binding_test.go index 1733617724..17df7dc59d 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -20,8 +20,8 @@ import ( "time" "github.com/gin-gonic/gin/testdata/protoexample" - "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" ) type appkey struct { @@ -832,7 +832,6 @@ func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, ba assert.Equal(t, 1, obj.Page) assert.Equal(t, 2, obj.Size) assert.Equal(t, "test-appkey", obj.Appkey) - } func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) { diff --git a/binding/form_mapping.go b/binding/form_mapping.go index cb66dd4a70..54b3eabd6c 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -26,7 +26,7 @@ var ( ErrConvertToMapString = errors.New("can not convert to map of strings") ) -func mapUri(ptr interface{}, m map[string][]string) error { +func mapURI(ptr interface{}, m map[string][]string) error { return mapFormByTag(ptr, m, "uri") } @@ -83,7 +83,7 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag return false, nil } - var vKind = value.Kind() + vKind := value.Kind() if vKind == reflect.Ptr { var isNew bool @@ -310,7 +310,6 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val t := time.Unix(tv/int64(d), tv%int64(d)) value.Set(reflect.ValueOf(t)) return nil - } if val == "" { diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 608594e28d..516554eb7d 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -131,7 +131,7 @@ func TestMappingURI(t *testing.T) { var s struct { F int `uri:"field"` } - err := mapUri(&s, map[string][]string{"field": {"6"}}) + err := mapURI(&s, map[string][]string{"field": {"6"}}) assert.NoError(t, err) assert.Equal(t, int(6), s.F) } diff --git a/binding/protobuf.go b/binding/protobuf.go index f9ece928d4..ca02897a9f 100644 --- a/binding/protobuf.go +++ b/binding/protobuf.go @@ -8,7 +8,7 @@ import ( "io/ioutil" "net/http" - "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/proto" ) type protobufBinding struct{} diff --git a/binding/uri.go b/binding/uri.go index f91ec38199..a3c0df515c 100644 --- a/binding/uri.go +++ b/binding/uri.go @@ -11,7 +11,7 @@ func (uriBinding) Name() string { } func (uriBinding) BindUri(m map[string][]string, obj interface{}) error { - if err := mapUri(obj, m); err != nil { + if err := mapURI(obj, m); err != nil { return err } return validate(obj) diff --git a/context_test.go b/context_test.go index ffbe5ccaa3..176eaae6b6 100644 --- a/context_test.go +++ b/context_test.go @@ -23,8 +23,8 @@ import ( "github.com/gin-contrib/sse" "github.com/gin-gonic/gin/binding" - "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" testdata "github.com/gin-gonic/gin/testdata/protoexample" ) @@ -234,7 +234,6 @@ func TestContextSetGetValues(t *testing.T) { assert.Exactly(t, c.MustGet("float32").(float32), float32(4.2)) assert.Exactly(t, c.MustGet("float64").(float64), 4.2) assert.Exactly(t, c.MustGet("intInterface").(int), 1) - } func TestContextGetString(t *testing.T) { @@ -300,7 +299,7 @@ func TestContextGetStringSlice(t *testing.T) { func TestContextGetStringMap(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - var m = make(map[string]interface{}) + m := make(map[string]interface{}) m["foo"] = 1 c.Set("map", m) @@ -310,7 +309,7 @@ func TestContextGetStringMap(t *testing.T) { func TestContextGetStringMapString(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - var m = make(map[string]string) + m := make(map[string]string) m["foo"] = "bar" c.Set("map", m) @@ -320,7 +319,7 @@ func TestContextGetStringMapString(t *testing.T) { func TestContextGetStringMapStringSlice(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - var m = make(map[string][]string) + m := make(map[string][]string) m["foo"] = []string{"foo"} c.Set("map", m) @@ -369,15 +368,12 @@ func TestContextHandlerNames(t *testing.T) { } func handlerNameTest(c *Context) { - } func handlerNameTest2(c *Context) { - } var handlerTest HandlerFunc = func(c *Context) { - } func TestContextHandler(t *testing.T) { @@ -659,8 +655,7 @@ func TestContextBodyAllowedForStatus(t *testing.T) { assert.True(t, true, bodyAllowedForStatus(http.StatusInternalServerError)) } -type TestPanicRender struct { -} +type TestPanicRender struct{} func (*TestPanicRender) Render(http.ResponseWriter) error { return errors.New("TestPanicRender") @@ -1329,7 +1324,7 @@ func TestContextAbortWithStatusJSON(t *testing.T) { _, err := buf.ReadFrom(w.Body) assert.NoError(t, err) jsonStringBody := buf.String() - assert.Equal(t, fmt.Sprint("{\"foo\":\"fooValue\",\"bar\":\"barValue\"}"), jsonStringBody) + assert.Equal(t, "{\"foo\":\"fooValue\",\"bar\":\"barValue\"}", jsonStringBody) } func TestContextError(t *testing.T) { @@ -1545,6 +1540,7 @@ func TestContextBindWithJSON(t *testing.T) { assert.Equal(t, "bar", obj.Foo) assert.Equal(t, 0, w.Body.Len()) } + func TestContextBindWithXML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) @@ -2100,6 +2096,8 @@ func TestContextWithFallbackErrFromRequestContext(t *testing.T) { assert.EqualError(t, c2.Err(), context.Canceled.Error()) } +type contextKey string + func TestContextWithFallbackValueFromRequestContext(t *testing.T) { tests := []struct { name string @@ -2122,8 +2120,8 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { getContextAndKey: func() (*Context, interface{}) { c := &Context{} c.Request, _ = http.NewRequest("POST", "/", nil) - c.Request = c.Request.WithContext(context.WithValue(context.TODO(), "key", "value")) - return c, "key" + c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value")) + return c, contextKey("key") }, value: "value", }, diff --git a/go.mod b/go.mod index 9484b26442..bf68259328 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,12 @@ go 1.13 require ( github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.6.1 - github.com/goccy/go-json v0.5.1 - github.com/golang/protobuf v1.3.3 - github.com/json-iterator/go v1.1.9 - github.com/mattn/go-isatty v0.0.12 - github.com/stretchr/testify v1.4.0 + github.com/go-playground/validator/v10 v10.9.0 + github.com/goccy/go-json v0.7.6 + github.com/json-iterator/go v1.1.11 + github.com/mattn/go-isatty v0.0.13 + github.com/stretchr/testify v1.7.0 github.com/ugorji/go/codec v1.2.6 - gopkg.in/yaml.v2 v2.2.8 + google.golang.org/protobuf v1.27.1 + gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index e61ef908a0..351ee253da 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -5,51 +6,76 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.6.1 h1:W6TRDXt4WcWp4c4nf/G+6BkGdhiIo0k417gfr+V6u4I= -github.com/go-playground/validator/v10 v10.6.1/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= -github.com/goccy/go-json v0.5.1 h1:R9UYTOUvo7eIY9aeDMZ4L6OVtHaSr1k2No9W6MKjXrA= -github.com/goccy/go-json v0.5.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +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.6 h1:H0wq4jppBQ+9222sk5+hPLL25abZQiRuQ6YPnjO9c+A= +github.com/goccy/go-json v0.7.6/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= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +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/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 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/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +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/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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +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-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= +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/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/mode.go b/mode.go index c8813aff26..4d199df306 100644 --- a/mode.go +++ b/mode.go @@ -41,8 +41,10 @@ var DefaultWriter io.Writer = os.Stdout // DefaultErrorWriter is the default io.Writer used by Gin to debug errors var DefaultErrorWriter io.Writer = os.Stderr -var ginMode = debugCode -var modeName = DebugMode +var ( + ginMode = debugCode + modeName = DebugMode +) func init() { mode := os.Getenv(EnvGinMode) diff --git a/render/json.go b/render/json.go index e25415b001..b80adcff34 100644 --- a/render/json.go +++ b/render/json.go @@ -49,7 +49,7 @@ type PureJSON struct { var ( jsonContentType = []string{"application/json; charset=utf-8"} jsonpContentType = []string{"application/javascript; charset=utf-8"} - jsonAsciiContentType = []string{"application/json"} + jsonASCIIContentType = []string{"application/json"} ) // Render (JSON) writes data with custom ContentType. @@ -178,7 +178,7 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) { // WriteContentType (AsciiJSON) writes JSON ContentType. func (r AsciiJSON) WriteContentType(w http.ResponseWriter) { - writeContentType(w, jsonAsciiContentType) + writeContentType(w, jsonASCIIContentType) } // Render (PureJSON) writes custom ContentType and encodes the given interface object. diff --git a/render/protobuf.go b/render/protobuf.go index 15aca9959c..1d2aa871c7 100644 --- a/render/protobuf.go +++ b/render/protobuf.go @@ -7,7 +7,7 @@ package render import ( "net/http" - "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/proto" ) // ProtoBuf contains the given interface object. diff --git a/render/render_test.go b/render/render_test.go index 353c82bbf2..22e7d5ab01 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -14,8 +14,8 @@ import ( "strings" "testing" - "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" testdata "github.com/gin-gonic/gin/testdata/protoexample" ) @@ -420,7 +420,8 @@ func TestRenderHTMLTemplateEmptyName(t *testing.T) { func TestRenderHTMLDebugFiles(t *testing.T) { w := httptest.NewRecorder() - htmlRender := HTMLDebug{Files: []string{"../testdata/template/hello.tmpl"}, + htmlRender := HTMLDebug{ + Files: []string{"../testdata/template/hello.tmpl"}, Glob: "", Delims: Delims{Left: "{[{", Right: "}]}"}, FuncMap: nil, @@ -438,7 +439,8 @@ func TestRenderHTMLDebugFiles(t *testing.T) { func TestRenderHTMLDebugGlob(t *testing.T) { w := httptest.NewRecorder() - htmlRender := HTMLDebug{Files: nil, + htmlRender := HTMLDebug{ + Files: nil, Glob: "../testdata/template/hello*", Delims: Delims{Left: "{[{", Right: "}]}"}, FuncMap: nil, @@ -455,7 +457,8 @@ func TestRenderHTMLDebugGlob(t *testing.T) { } func TestRenderHTMLDebugPanics(t *testing.T) { - htmlRender := HTMLDebug{Files: nil, + htmlRender := HTMLDebug{ + Files: nil, Glob: "", Delims: Delims{"{{", "}}"}, FuncMap: nil, diff --git a/response_writer_test.go b/response_writer_test.go index 1f113e74d6..9061d02101 100644 --- a/response_writer_test.go +++ b/response_writer_test.go @@ -17,12 +17,14 @@ import ( // func (w *responseWriter) CloseNotify() <-chan bool { // func (w *responseWriter) Flush() { -var _ ResponseWriter = &responseWriter{} -var _ http.ResponseWriter = &responseWriter{} -var _ http.ResponseWriter = ResponseWriter(&responseWriter{}) -var _ http.Hijacker = ResponseWriter(&responseWriter{}) -var _ http.Flusher = ResponseWriter(&responseWriter{}) -var _ http.CloseNotifier = ResponseWriter(&responseWriter{}) +var ( + _ ResponseWriter = &responseWriter{} + _ http.ResponseWriter = &responseWriter{} + _ http.ResponseWriter = ResponseWriter(&responseWriter{}) + _ http.Hijacker = ResponseWriter(&responseWriter{}) + _ http.Flusher = ResponseWriter(&responseWriter{}) + _ http.CloseNotifier = ResponseWriter(&responseWriter{}) +) func init() { SetMode(TestMode) diff --git a/testdata/protoexample/test.pb.go b/testdata/protoexample/test.pb.go index 21997ca1c6..bf45e02891 100644 --- a/testdata/protoexample/test.pb.go +++ b/testdata/protoexample/test.pb.go @@ -1,24 +1,24 @@ -// Code generated by protoc-gen-go. +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.0 +// protoc v3.15.8 // source: test.proto -// DO NOT EDIT! -/* -Package protoexample is a generated protocol buffer package. - -It is generated from these files: - test.proto - -It has these top-level messages: - Test -*/ package protoexample -import proto "github.com/golang/protobuf/proto" -import math "math" +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) type FOO int32 @@ -26,88 +26,273 @@ const ( FOO_X FOO = 17 ) -var FOO_name = map[int32]string{ - 17: "X", -} -var FOO_value = map[string]int32{ - "X": 17, -} +// Enum value maps for FOO. +var ( + FOO_name = map[int32]string{ + 17: "X", + } + FOO_value = map[string]int32{ + "X": 17, + } +) func (x FOO) Enum() *FOO { p := new(FOO) *p = x return p } + func (x FOO) String() string { - return proto.EnumName(FOO_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FOO) Descriptor() protoreflect.EnumDescriptor { + return file_test_proto_enumTypes[0].Descriptor() +} + +func (FOO) Type() protoreflect.EnumType { + return &file_test_proto_enumTypes[0] +} + +func (x FOO) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -func (x *FOO) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(FOO_value, data, "FOO") + +// Deprecated: Do not use. +func (x *FOO) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = FOO(value) + *x = FOO(num) return nil } +// Deprecated: Use FOO.Descriptor instead. +func (FOO) EnumDescriptor() ([]byte, []int) { + return file_test_proto_rawDescGZIP(), []int{0} +} + type Test struct { - Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` - Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` - Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` - Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` - XXX_unrecognized []byte `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` + Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` + Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` + Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup,json=optionalgroup" json:"optionalgroup,omitempty"` +} + +// Default values for Test fields. +const ( + Default_Test_Type = int32(77) +) + +func (x *Test) Reset() { + *x = Test{} + if protoimpl.UnsafeEnabled { + mi := &file_test_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Test) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Test) Reset() { *m = Test{} } -func (m *Test) String() string { return proto.CompactTextString(m) } -func (*Test) ProtoMessage() {} +func (*Test) ProtoMessage() {} -const Default_Test_Type int32 = 77 +func (x *Test) ProtoReflect() protoreflect.Message { + mi := &file_test_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} -func (m *Test) GetLabel() string { - if m != nil && m.Label != nil { - return *m.Label +// Deprecated: Use Test.ProtoReflect.Descriptor instead. +func (*Test) Descriptor() ([]byte, []int) { + return file_test_proto_rawDescGZIP(), []int{0} +} + +func (x *Test) GetLabel() string { + if x != nil && x.Label != nil { + return *x.Label } return "" } -func (m *Test) GetType() int32 { - if m != nil && m.Type != nil { - return *m.Type +func (x *Test) GetType() int32 { + if x != nil && x.Type != nil { + return *x.Type } return Default_Test_Type } -func (m *Test) GetReps() []int64 { - if m != nil { - return m.Reps +func (x *Test) GetReps() []int64 { + if x != nil { + return x.Reps } return nil } -func (m *Test) GetOptionalgroup() *Test_OptionalGroup { - if m != nil { - return m.Optionalgroup +func (x *Test) GetOptionalgroup() *Test_OptionalGroup { + if x != nil { + return x.Optionalgroup } return nil } type Test_OptionalGroup struct { - RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` - XXX_unrecognized []byte `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequiredField *string `protobuf:"bytes,5,req,name=RequiredField" json:"RequiredField,omitempty"` +} + +func (x *Test_OptionalGroup) Reset() { + *x = Test_OptionalGroup{} + if protoimpl.UnsafeEnabled { + mi := &file_test_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Test_OptionalGroup) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Test_OptionalGroup) ProtoMessage() {} + +func (x *Test_OptionalGroup) ProtoReflect() protoreflect.Message { + mi := &file_test_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} } -func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) } -func (*Test_OptionalGroup) ProtoMessage() {} +// Deprecated: Use Test_OptionalGroup.ProtoReflect.Descriptor instead. +func (*Test_OptionalGroup) Descriptor() ([]byte, []int) { + return file_test_proto_rawDescGZIP(), []int{0, 0} +} -func (m *Test_OptionalGroup) GetRequiredField() string { - if m != nil && m.RequiredField != nil { - return *m.RequiredField +func (x *Test_OptionalGroup) GetRequiredField() string { + if x != nil && x.RequiredField != nil { + return *x.RequiredField } return "" } -func init() { - proto.RegisterEnum("protoexample.FOO", FOO_name, FOO_value) +var File_test_proto protoreflect.FileDescriptor + +var file_test_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x22, 0xc7, 0x01, 0x0a, 0x04, 0x54, + 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x02, + 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x02, 0x37, 0x37, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x03, 0x52, + 0x04, 0x72, 0x65, 0x70, 0x73, 0x12, 0x46, 0x0a, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0a, 0x32, 0x20, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, + 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x0d, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x35, 0x0a, + 0x0d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x24, + 0x0a, 0x0d, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, + 0x05, 0x20, 0x02, 0x28, 0x09, 0x52, 0x0d, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x2a, 0x0c, 0x0a, 0x03, 0x46, 0x4f, 0x4f, 0x12, 0x05, 0x0a, 0x01, 0x58, + 0x10, 0x11, +} + +var ( + file_test_proto_rawDescOnce sync.Once + file_test_proto_rawDescData = file_test_proto_rawDesc +) + +func file_test_proto_rawDescGZIP() []byte { + file_test_proto_rawDescOnce.Do(func() { + file_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto_rawDescData) + }) + return file_test_proto_rawDescData +} + +var file_test_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_test_proto_goTypes = []interface{}{ + (FOO)(0), // 0: protoexample.FOO + (*Test)(nil), // 1: protoexample.Test + (*Test_OptionalGroup)(nil), // 2: protoexample.Test.OptionalGroup +} +var file_test_proto_depIdxs = []int32{ + 2, // 0: protoexample.Test.optionalgroup:type_name -> protoexample.Test.OptionalGroup + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_test_proto_init() } +func file_test_proto_init() { + if File_test_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Test); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Test_OptionalGroup); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_test_proto_rawDesc, + NumEnums: 1, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_test_proto_goTypes, + DependencyIndexes: file_test_proto_depIdxs, + EnumInfos: file_test_proto_enumTypes, + MessageInfos: file_test_proto_msgTypes, + }.Build() + File_test_proto = out.File + file_test_proto_rawDesc = nil + file_test_proto_goTypes = nil + file_test_proto_depIdxs = nil } diff --git a/tree_test.go b/tree_test.go index ea13c30ee9..cbb37340ef 100644 --- a/tree_test.go +++ b/tree_test.go @@ -843,7 +843,7 @@ func TestTreeInvalidParamsType(t *testing.T) { tree.children[0].nType = 2 // set invalid Params type - params := make(Params, 0, 0) + params := make(Params, 0) // try to trigger slice bounds out of range with capacity 0 tree.getValue("/test", ¶ms, false) From a46dee3a9af4edd9de1694e3ba3592b20c6a8968 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Thu, 19 Aug 2021 10:16:18 +0200 Subject: [PATCH 023/346] Update .golangci.yml (#2829) --- .golangci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index 1c3cb758e0..cfcff00cc0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,3 +1,5 @@ +run: + timeout: 5m linters: enable: - gofmt From f3a6b69fd00d7dba9e7cdb726d3c0fc513259834 Mon Sep 17 00:00:00 2001 From: Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com> Date: Fri, 20 Aug 2021 03:38:24 +0300 Subject: [PATCH 024/346] Delete unused static const (#2830) --- tree.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tree.go b/tree.go index eb549591d6..fb0a5935c2 100644 --- a/tree.go +++ b/tree.go @@ -101,8 +101,7 @@ func countParams(path string) uint16 { type nodeType uint8 const ( - static nodeType = iota // default - root + root nodeType = iota + 1 param catchAll ) From 527d950252c03e6c79233cbd7f24f07f81ef3cc6 Mon Sep 17 00:00:00 2001 From: Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com> Date: Sat, 21 Aug 2021 09:59:17 +0300 Subject: [PATCH 025/346] Delete unused arg (#2834) --- binding/form_mapping.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 54b3eabd6c..f8b4b12393 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -210,7 +210,7 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel case reflect.Int64: switch value.Interface().(type) { case time.Duration: - return setTimeDuration(val, value, field) + return setTimeDuration(val, value) } return setIntField(val, 64, value) case reflect.Uint: @@ -359,7 +359,7 @@ func setSlice(vals []string, value reflect.Value, field reflect.StructField) err return nil } -func setTimeDuration(val string, value reflect.Value, field reflect.StructField) error { +func setTimeDuration(val string, value reflect.Value) error { d, err := time.ParseDuration(val) if err != nil { return err From c30302056176572dc924b89bcb7077de8ffc5000 Mon Sep 17 00:00:00 2001 From: Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com> Date: Sat, 21 Aug 2021 16:45:30 +0300 Subject: [PATCH 026/346] bump golangci-lint (#2839) --- .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 ab450ab834..21895f2899 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.41.1 + version: v1.42.0 args: --verbose test: strategy: From 820090381719dc209584c3fc1044cd95ec520594 Mon Sep 17 00:00:00 2001 From: Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com> Date: Sat, 21 Aug 2021 16:46:52 +0300 Subject: [PATCH 027/346] Delete deadcode exclude rules (#2838) --- .golangci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index cfcff00cc0..78a4259a18 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,9 +7,6 @@ linters: - revive issues: exclude-rules: - - linters: - - deadcode - text: "`static` is unused" - linters: - structcheck - unused From dfc25f91e0a09099fc6c9f9166bab9044af16143 Mon Sep 17 00:00:00 2001 From: Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com> Date: Sun, 22 Aug 2021 04:29:51 +0300 Subject: [PATCH 028/346] Add short func with named return (#2837) --- context.go | 52 ++++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/context.go b/context.go index 1a953006c0..56c20f2c61 100644 --- a/context.go +++ b/context.go @@ -391,9 +391,9 @@ func (c *Context) Param(key string) string { // c.Query("name") == "Manu" // c.Query("value") == "" // c.Query("wtf") == "" -func (c *Context) Query(key string) string { - value, _ := c.GetQuery(key) - return value +func (c *Context) Query(key string) (value string) { + value, _ = c.GetQuery(key) + return } // DefaultQuery returns the keyed url query value if it exists, @@ -427,9 +427,9 @@ func (c *Context) GetQuery(key string) (string, bool) { // QueryArray returns a slice of strings for a given query key. // The length of the slice depends on the number of params with the given key. -func (c *Context) QueryArray(key string) []string { - values, _ := c.GetQueryArray(key) - return values +func (c *Context) QueryArray(key string) (values []string) { + values, _ = c.GetQueryArray(key) + return } func (c *Context) initQueryCache() { @@ -444,18 +444,16 @@ func (c *Context) initQueryCache() { // GetQueryArray returns a slice of strings for a given query key, plus // a boolean value whether at least one value exists for the given key. -func (c *Context) GetQueryArray(key string) ([]string, bool) { +func (c *Context) GetQueryArray(key string) (values []string, ok bool) { c.initQueryCache() - if values, ok := c.queryCache[key]; ok && len(values) > 0 { - return values, true - } - return []string{}, false + values, ok = c.queryCache[key] + return } // QueryMap returns a map for a given query key. -func (c *Context) QueryMap(key string) map[string]string { - dicts, _ := c.GetQueryMap(key) - return dicts +func (c *Context) QueryMap(key string) (dicts map[string]string) { + dicts, _ = c.GetQueryMap(key) + return } // GetQueryMap returns a map for a given query key, plus a boolean value @@ -467,9 +465,9 @@ func (c *Context) GetQueryMap(key string) (map[string]string, bool) { // PostForm returns the specified key from a POST urlencoded form or multipart form // when it exists, otherwise it returns an empty string `("")`. -func (c *Context) PostForm(key string) string { - value, _ := c.GetPostForm(key) - return value +func (c *Context) PostForm(key string) (value string) { + value, _ = c.GetPostForm(key) + return } // DefaultPostForm returns the specified key from a POST urlencoded form or multipart form @@ -498,9 +496,9 @@ func (c *Context) GetPostForm(key string) (string, bool) { // PostFormArray returns a slice of strings for a given form key. // The length of the slice depends on the number of params with the given key. -func (c *Context) PostFormArray(key string) []string { - values, _ := c.GetPostFormArray(key) - return values +func (c *Context) PostFormArray(key string) (values []string) { + values, _ = c.GetPostFormArray(key) + return } func (c *Context) initFormCache() { @@ -518,18 +516,16 @@ func (c *Context) initFormCache() { // GetPostFormArray returns a slice of strings for a given form key, plus // a boolean value whether at least one value exists for the given key. -func (c *Context) GetPostFormArray(key string) ([]string, bool) { +func (c *Context) GetPostFormArray(key string) (values []string, ok bool) { c.initFormCache() - if values := c.formCache[key]; len(values) > 0 { - return values, true - } - return []string{}, false + values, ok = c.formCache[key] + return } // PostFormMap returns a map for a given form key. -func (c *Context) PostFormMap(key string) map[string]string { - dicts, _ := c.GetPostFormMap(key) - return dicts +func (c *Context) PostFormMap(key string) (dicts map[string]string) { + dicts, _ = c.GetPostFormMap(key) + return } // GetPostFormMap returns a map for a given form key, plus a boolean value From 4e7584175d7f2b4245249e769110fd1df0d779db Mon Sep 17 00:00:00 2001 From: Lanco <35420416+lancoLiu@users.noreply.github.com> Date: Mon, 23 Aug 2021 01:32:41 +0800 Subject: [PATCH 029/346] minor tweaks,optimize code (#2788) --- render/json.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/render/json.go b/render/json.go index b80adcff34..3ebcee9706 100644 --- a/render/json.go +++ b/render/json.go @@ -102,8 +102,7 @@ func (r SecureJSON) Render(w http.ResponseWriter) error { // if the jsonBytes is array values if bytes.HasPrefix(jsonBytes, bytesconv.StringToBytes("[")) && bytes.HasSuffix(jsonBytes, bytesconv.StringToBytes("]")) { - _, err = w.Write(bytesconv.StringToBytes(r.Prefix)) - if err != nil { + if _, err = w.Write(bytesconv.StringToBytes(r.Prefix)); err != nil { return err } } @@ -130,20 +129,19 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) { } callback := template.JSEscapeString(r.Callback) - _, err = w.Write(bytesconv.StringToBytes(callback)) - if err != nil { + if _, err = w.Write(bytesconv.StringToBytes(callback)); err != nil { return err } - _, err = w.Write(bytesconv.StringToBytes("(")) - if err != nil { + + if _, err = w.Write(bytesconv.StringToBytes("(")); err != nil { return err } - _, err = w.Write(ret) - if err != nil { + + if _, err = w.Write(ret); err != nil { return err } - _, err = w.Write(bytesconv.StringToBytes(");")) - if err != nil { + + if _, err = w.Write(bytesconv.StringToBytes(");")); err != nil { return err } From 30cdbfcf4c904bb2bcc0f474ff1660a1efb852da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Aug 2021 09:58:54 +0800 Subject: [PATCH 030/346] Bump github.com/goccy/go-json from 0.7.6 to 0.7.7 (#2849) Bumps [github.com/goccy/go-json](https://github.com/goccy/go-json) from 0.7.6 to 0.7.7. - [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.6...v0.7.7) --- updated-dependencies: - dependency-name: github.com/goccy/go-json dependency-type: direct:production update-type: version-update:semver-patch ... 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 bf68259328..844dc453cd 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.6 + github.com/goccy/go-json v0.7.7 github.com/json-iterator/go v1.1.11 github.com/mattn/go-isatty v0.0.13 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 351ee253da..266f4ac5cb 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.6 h1:H0wq4jppBQ+9222sk5+hPLL25abZQiRuQ6YPnjO9c+A= -github.com/goccy/go-json v0.7.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.7.7 h1:MflzqwHECILPg/0qDYB+jx+sJeNojJHEbRYsa8q7j/o= +github.com/goccy/go-json v0.7.7/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 e4c026e2a101de574c480d390da41b91efb9490e Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 6 Sep 2021 08:10:06 +0800 Subject: [PATCH 031/346] Fix go1.17 test error (#2856) --- context_1.16_test.go | 31 +++++++++++++++++++++++++++++++ context_1.17_test.go | 33 +++++++++++++++++++++++++++++++++ context_test.go | 13 ------------- 3 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 context_1.16_test.go create mode 100644 context_1.17_test.go diff --git a/context_1.16_test.go b/context_1.16_test.go new file mode 100644 index 0000000000..053e6c5aab --- /dev/null +++ b/context_1.16_test.go @@ -0,0 +1,31 @@ +// Copyright 2021 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build !go1.17 +// +build !go1.17 + +package gin + +import ( + "bytes" + "mime/multipart" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestContextFormFileFailed16(t *testing.T) { + buf := new(bytes.Buffer) + mw := multipart.NewWriter(buf) + mw.Close() + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request.Header.Set("Content-Type", mw.FormDataContentType()) + c.engine.MaxMultipartMemory = 8 << 20 + f, err := c.FormFile("file") + assert.Error(t, err) + assert.Nil(t, f) +} diff --git a/context_1.17_test.go b/context_1.17_test.go new file mode 100644 index 0000000000..431d54c7bf --- /dev/null +++ b/context_1.17_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build go1.17 +// +build go1.17 + +package gin + +import ( + "bytes" + "mime/multipart" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestContextFormFileFailed17(t *testing.T) { + buf := new(bytes.Buffer) + mw := multipart.NewWriter(buf) + mw.Close() + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request.Header.Set("Content-Type", mw.FormDataContentType()) + c.engine.MaxMultipartMemory = 8 << 20 + assert.Panics(t, func() { + f, err := c.FormFile("file") + assert.Error(t, err) + assert.Nil(t, f) + }) +} diff --git a/context_test.go b/context_test.go index 176eaae6b6..7c5626e1be 100644 --- a/context_test.go +++ b/context_test.go @@ -87,19 +87,6 @@ func TestContextFormFile(t *testing.T) { assert.NoError(t, c.SaveUploadedFile(f, "test")) } -func TestContextFormFileFailed(t *testing.T) { - buf := new(bytes.Buffer) - mw := multipart.NewWriter(buf) - mw.Close() - c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) - c.Request.Header.Set("Content-Type", mw.FormDataContentType()) - c.engine.MaxMultipartMemory = 8 << 20 - f, err := c.FormFile("file") - assert.Error(t, err) - assert.Nil(t, f) -} - func TestContextMultipartForm(t *testing.T) { buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) From a550c568d7416df89c6b29615a6f977880c668f2 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 6 Sep 2021 08:21:03 +0800 Subject: [PATCH 032/346] chore: Add go1.17 for testing (#2828) Signed-off-by: Bo-Yi Wu --- .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 21895f2899..0ebe2ec560 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: [1.13, 1.14, 1.15, 1.16] + go: [1.13, 1.14, 1.15, 1.16, 1.17] test-tags: ['', nomsgpack] include: - os: ubuntu-latest From abcf32f5add9f52704b9aad6e554411b0716e242 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Sep 2021 06:43:08 +0800 Subject: [PATCH 033/346] Bump github.com/goccy/go-json from 0.7.7 to 0.7.8 (#2859) Bumps [github.com/goccy/go-json](https://github.com/goccy/go-json) from 0.7.7 to 0.7.8. - [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.7...v0.7.8) --- updated-dependencies: - dependency-name: github.com/goccy/go-json dependency-type: direct:production update-type: version-update:semver-patch ... 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 844dc453cd..26b927101d 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.7 + github.com/goccy/go-json v0.7.8 github.com/json-iterator/go v1.1.11 github.com/mattn/go-isatty v0.0.13 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 266f4ac5cb..8aaf41a352 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.7 h1:MflzqwHECILPg/0qDYB+jx+sJeNojJHEbRYsa8q7j/o= -github.com/goccy/go-json v0.7.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.7.8 h1:CvMH7LotYymYuLGEohBM1lTZWX4g6jzWUUl2aLFuBoE= +github.com/goccy/go-json v0.7.8/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 5c62979390881dcaf31e4565d6cc4e5d3e211afc Mon Sep 17 00:00:00 2001 From: Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com> Date: Tue, 7 Sep 2021 01:43:42 +0300 Subject: [PATCH 034/346] bump golangci-lint version (#2858) --- .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 0ebe2ec560..6eb5a64d1f 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.0 + version: v1.42.1 args: --verbose test: strategy: From deb83b6365cf1b95f50929492f6058c94a6ff274 Mon Sep 17 00:00:00 2001 From: filikos <11477309+filikos@users.noreply.github.com> Date: Tue, 7 Sep 2021 04:08:45 +0200 Subject: [PATCH 035/346] gin.Context.SetParam shortcut for e2e tests (#2848) * Added SetParam shortcut for e2e tests, added SetParam test * Adjusted naming and formatting * fixed typo --- context.go | 9 +++++++++ context_test.go | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/context.go b/context.go index 56c20f2c61..62849488b2 100644 --- a/context.go +++ b/context.go @@ -383,6 +383,15 @@ func (c *Context) Param(key string) string { return c.Params.ByName(key) } +// AddParam adds param to context and +// replaces path param key with given value for e2e testing purposes +// Example Route: "/user/:id" +// AddParam("id", 1) +// Result: "/user/1" +func (c *Context) AddParam(key, value string) { + c.Params = append(c.Params, Param{Key: key, Value: value}) +} + // Query returns the keyed url query value if it exists, // otherwise it returns an empty string `("")`. // It is shortcut for `c.Request.URL.Query().Get(key)` diff --git a/context_test.go b/context_test.go index 7c5626e1be..f9792e326f 100644 --- a/context_test.go +++ b/context_test.go @@ -2137,3 +2137,14 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { }) } } + +func TestContextAddParam(t *testing.T) { + c := &Context{} + id := "id" + value := "1" + c.AddParam(id, value) + + v, ok := c.Params.Get(id) + assert.Equal(t, ok, true) + assert.Equal(t, value, v) +} From eab47b5423b67608ffe099ce2f851f3d48ef4a96 Mon Sep 17 00:00:00 2001 From: Tevic Date: Tue, 7 Sep 2021 10:10:32 +0800 Subject: [PATCH 036/346] fix: check obj type in protobufBinding (#2851) * fix: check obj type in protobufBinding * fix: UnitTest for invalid proto obj --- binding/binding_test.go | 7 +++++++ binding/protobuf.go | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/binding/binding_test.go b/binding/binding_test.go index 17df7dc59d..5b0ce39d3e 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -1339,6 +1339,13 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body err := b.Bind(req, &obj) assert.Error(t, err) + invalid_obj := FooStruct{} + req.Body = ioutil.NopCloser(strings.NewReader(`{"msg":"hello"}`)) + req.Header.Add("Content-Type", MIMEPROTOBUF) + err = b.Bind(req, &invalid_obj) + assert.Error(t, err) + assert.Equal(t, err.Error(), "obj is not ProtoMessage") + obj = protoexample.Test{} req = requestWithBody("POST", badPath, badBody) req.Header.Add("Content-Type", MIMEPROTOBUF) diff --git a/binding/protobuf.go b/binding/protobuf.go index ca02897a9f..a4e471535c 100644 --- a/binding/protobuf.go +++ b/binding/protobuf.go @@ -5,6 +5,7 @@ package binding import ( + "errors" "io/ioutil" "net/http" @@ -26,7 +27,11 @@ func (b protobufBinding) Bind(req *http.Request, obj interface{}) error { } func (protobufBinding) BindBody(body []byte, obj interface{}) error { - if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil { + msg, ok := obj.(proto.Message) + if !ok { + return errors.New("obj is not ProtoMessage") + } + if err := proto.Unmarshal(body, msg); err != nil { return err } // Here it's same to return validate(obj), but util now we can't add From ae349b4015f4736e44ea813365dcf51094329b95 Mon Sep 17 00:00:00 2001 From: wssccc Date: Tue, 7 Sep 2021 13:05:19 +0800 Subject: [PATCH 037/346] Fix typo (#2860) --- tree_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree_test.go b/tree_test.go index cbb37340ef..8ae5b7db65 100644 --- a/tree_test.go +++ b/tree_test.go @@ -437,7 +437,7 @@ func TestTreeChildConflict(t *testing.T) { testRoutes(t, routes) } -func TestTreeDupliatePath(t *testing.T) { +func TestTreeDuplicatePath(t *testing.T) { tree := &node{} routes := [...]string{ From 3a6f18f32f22d7978bbafdf9b81d3a568b7a5868 Mon Sep 17 00:00:00 2001 From: Henry Yee Date: Wed, 8 Sep 2021 11:30:55 +0800 Subject: [PATCH 038/346] fixed SetOutput() panics on go 1.17 (#2861) * fixed SetOutput() panics on go 1.17 * update go.sum --- go.mod | 2 +- go.sum | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 26b927101d..92cca49f79 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/go-playground/validator/v10 v10.9.0 github.com/goccy/go-json v0.7.8 github.com/json-iterator/go v1.1.11 - 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/ugorji/go/codec v1.2.6 google.golang.org/protobuf v1.27.1 diff --git a/go.sum b/go.sum index 8aaf41a352..969d49c0e5 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 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/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +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/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= @@ -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/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 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-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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From 71f70870976cf304cdb3942a871c15a20f5d35c5 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Tue, 21 Sep 2021 09:22:21 +0200 Subject: [PATCH 039/346] golangci(lint) : more linters (#2870) --- .github/workflows/gin.yml | 1 + .golangci.yml | 13 +++++++++++++ binding/form.go | 3 ++- context.go | 5 +++-- context_test.go | 3 +-- recovery.go | 4 +++- render/render_test.go | 3 +-- 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 6eb5a64d1f..15c2530aa1 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -24,6 +24,7 @@ jobs: version: v1.42.1 args: --verbose test: + needs: lint strategy: matrix: os: [ubuntu-latest, macos-latest] diff --git a/.golangci.yml b/.golangci.yml index 78a4259a18..ba5f4d1e50 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,9 +2,22 @@ run: timeout: 5m linters: enable: + - asciicheck + - depguard + - dogsled + - durationcheck + - errcheck + - errorlint + - exportloopref + - gci - gofmt + - goimports - misspell + - nakedret + - nilerr + - nolintlint - revive + - wastedassign issues: exclude-rules: - linters: diff --git a/binding/form.go b/binding/form.go index 040af9e209..fa2a6540a0 100644 --- a/binding/form.go +++ b/binding/form.go @@ -5,6 +5,7 @@ package binding import ( + "errors" "net/http" ) @@ -22,7 +23,7 @@ func (formBinding) Bind(req *http.Request, obj interface{}) error { if err := req.ParseForm(); err != nil { 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 } if err := mapForm(obj, req.Form); err != nil { diff --git a/context.go b/context.go index 62849488b2..8a2f46d1cb 100644 --- a/context.go +++ b/context.go @@ -220,7 +220,8 @@ func (c *Context) Error(err error) *Error { panic("err is nil") } - parsedError, ok := err.(*Error) + var parsedError *Error + ok := errors.As(err, &parsedError) if !ok { parsedError = &Error{ Err: err, @@ -515,7 +516,7 @@ func (c *Context) initFormCache() { c.formCache = make(url.Values) req := c.Request 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) } } diff --git a/context_test.go b/context_test.go index f9792e326f..ad16fbb1c6 100644 --- a/context_test.go +++ b/context_test.go @@ -23,10 +23,9 @@ import ( "github.com/gin-contrib/sse" "github.com/gin-gonic/gin/binding" + testdata "github.com/gin-gonic/gin/testdata/protoexample" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/proto" - - testdata "github.com/gin-gonic/gin/testdata/protoexample" ) var _ context.Context = &Context{} diff --git a/recovery.go b/recovery.go index 3101fe28a0..39f1355138 100644 --- a/recovery.go +++ b/recovery.go @@ -6,6 +6,7 @@ package gin import ( "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -60,7 +61,8 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc { // condition that warrants a panic stack trace. var brokenPipe bool 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") { brokenPipe = true } diff --git a/render/render_test.go b/render/render_test.go index 22e7d5ab01..e417731a89 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -14,10 +14,9 @@ import ( "strings" "testing" + testdata "github.com/gin-gonic/gin/testdata/protoexample" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/proto" - - testdata "github.com/gin-gonic/gin/testdata/protoexample" ) // TODO unit tests From e052bf31aac21ea25d970bb2f2f93112a904abdb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Sep 2021 15:23:31 +0800 Subject: [PATCH 040/346] Bump github.com/json-iterator/go from 1.1.11 to 1.1.12 (#2865) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 92cca49f79..80807e85be 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ 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.8 - github.com/json-iterator/go v1.1.11 + github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/stretchr/testify v1.7.0 github.com/ugorji/go/codec v1.2.6 diff --git a/go.sum b/go.sum index 969d49c0e5..33ce8cd709 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS 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/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.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= @@ -34,8 +34,8 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 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/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= From ef168679ce5e6c15aefb460787e977bacc3c38f3 Mon Sep 17 00:00:00 2001 From: Aravinth Sundaram Date: Tue, 28 Sep 2021 06:27:36 +0530 Subject: [PATCH 041/346] Correcting grammatical errors in README file (#2880) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 198bf01193..10be5e155d 100644 --- a/README.md +++ b/README.md @@ -1827,7 +1827,7 @@ func main() { quit := make(chan os.Signal) // kill (no param) default send syscall.SIGTERM // 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) <-quit log.Println("Shutting down server...") From d6534ccf3893b74738c78f51f8cb880c34db4ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=BB=E5=AF=BB=E8=A7=85=E8=A7=85=E7=9A=84Gopher?= Date: Tue, 28 Sep 2021 09:45:50 +0800 Subject: [PATCH 042/346] turn on HandleMethodNotAllowed when using NoMethod #2871 (#2872) --- gin.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 6ab2be66d0..ae6d479917 100644 --- a/gin.go +++ b/gin.go @@ -264,8 +264,9 @@ func (engine *Engine) NoRoute(handlers ...HandlerFunc) { engine.rebuild404Handlers() } -// NoMethod sets the handlers called when... TODO. +// NoMethod sets the handlers called when NoMethod. func (engine *Engine) NoMethod(handlers ...HandlerFunc) { + engine.HandleMethodNotAllowed = true engine.noMethod = handlers engine.rebuild405Handlers() } From f469c1be3925ab32bd38fbf944b230c70abe9e2b Mon Sep 17 00:00:00 2001 From: Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com> Date: Tue, 28 Sep 2021 06:37:31 +0300 Subject: [PATCH 043/346] Add gosec (#2882) --- .golangci.yml | 62 +++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index ba5f4d1e50..c5e1de388a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,34 +2,38 @@ run: timeout: 5m linters: enable: - - asciicheck - - depguard - - dogsled - - durationcheck - - errcheck - - errorlint - - exportloopref - - gci - - gofmt - - goimports - - misspell - - nakedret - - nilerr - - nolintlint - - revive - - wastedassign + - asciicheck + - depguard + - dogsled + - durationcheck + - errcheck + - errorlint + - exportloopref + - gci + - gofmt + - goimports + - gosec + - misspell + - nakedret + - nilerr + - nolintlint + - revive + - wastedassign issues: exclude-rules: - - linters: - - structcheck - - unused - text: "`data` is unused" - - linters: - - staticcheck - text: "SA1019:" - - linters: - - revive - text: "var-naming:" - - linters: - - revive - text: "exported:" + - linters: + - structcheck + - unused + text: "`data` is unused" + - linters: + - staticcheck + text: "SA1019:" + - linters: + - revive + text: "var-naming:" + - linters: + - revive + text: "exported:" + - path: _test\.go + linters: + - gosec # security is not make sense in tests From 97b3c0d88ada639763414f35715f88d6bd38fa10 Mon Sep 17 00:00:00 2001 From: joeADSP <75027008+joeADSP@users.noreply.github.com> Date: Wed, 29 Sep 2021 00:35:06 +0100 Subject: [PATCH 044/346] Fix grammatical and spelling errors in context.go (#2883) --- context.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 8a2f46d1cb..370f608634 100644 --- a/context.go +++ b/context.go @@ -735,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. // 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 the headers are nots syntactically valid OR the remote IP does not correspong to a trusted proxy, +// 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. func (c *Context) ClientIP() string { // Check if we're running on a trusted platform From 6d75aba83f7733d505b830bbe224583f1ef3771e Mon Sep 17 00:00:00 2001 From: Notealot <714804968@qq.com> Date: Wed, 29 Sep 2021 19:26:02 +0800 Subject: [PATCH 045/346] Quick Fix c.ClientIP() mistakely parsing to 127.0.0.1 for who not using r.Run() to run http server (#2832) --- gin.go | 4 ++-- middleware_test.go | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gin.go b/gin.go index ae6d479917..02a1062c31 100644 --- a/gin.go +++ b/gin.go @@ -183,6 +183,7 @@ func New() *Engine { trees: make(methodTrees, 0, 9), delims: render.Delims{Left: "{{", Right: "}}"}, secureJSONPrefix: "while(1);", + trustedCIDRs: []*net.IPNet{{IP: net.IP{0x0, 0x0, 0x0, 0x0}, Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}}}, } engine.RouterGroup.engine = engine engine.pool.New = func() interface{} { @@ -264,9 +265,8 @@ func (engine *Engine) NoRoute(handlers ...HandlerFunc) { engine.rebuild404Handlers() } -// NoMethod sets the handlers called when NoMethod. +// NoMethod sets the handlers called when Engine.HandleMethodNotAllowed = true. func (engine *Engine) NoMethod(handlers ...HandlerFunc) { - engine.HandleMethodNotAllowed = true engine.noMethod = handlers engine.rebuild405Handlers() } diff --git a/middleware_test.go b/middleware_test.go index fca1c530b3..4b4afd4af0 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -118,7 +118,10 @@ func TestMiddlewareNoMethodEnabled(t *testing.T) { func TestMiddlewareNoMethodDisabled(t *testing.T) { signature := "" router := New() + + // NoMethod disabled router.HandleMethodNotAllowed = false + router.Use(func(c *Context) { signature += "A" c.Next() @@ -144,6 +147,7 @@ func TestMiddlewareNoMethodDisabled(t *testing.T) { router.POST("/", func(c *Context) { signature += " XX " }) + // RUN w := performRequest(router, "GET", "/") From 1a2bc0e7cb1a69b7750bd652d92c4d2b41504f04 Mon Sep 17 00:00:00 2001 From: axiaoxin <254606826@qq.com> Date: Thu, 30 Sep 2021 10:04:28 +0800 Subject: [PATCH 046/346] =?UTF-8?q?setted=20typo=20fix:=20There=E2=80=99s?= =?UTF-8?q?=20no=20such=20word=20as=20`setted`,=20`set`=20is=20set,=20set,?= =?UTF-8?q?=20setting=20(#2886)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/form_mapping.go | 18 +++++++++--------- binding/multipart_form_mapping.go | 16 ++++++++-------- errors_test.go | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index f8b4b12393..fa7ad1bc4e 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -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 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 @@ -69,7 +69,7 @@ type formSource map[string][]string var _ setter = formSource(nil) // 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) } @@ -92,14 +92,14 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag isNew = true 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 { return false, err } - if isNew && isSetted { + if isNew && isSet { value.Set(vPtr) } - return isSetted, nil + return isSet, nil } 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 { tValue := value.Type() - var isSetted bool + var isSet bool for i := 0; i < value.NumField(); i++ { sf := tValue.Field(i) if sf.PkgPath != "" && !sf.Anonymous { // unexported @@ -125,9 +125,9 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag if err != nil { return false, err } - isSetted = isSetted || ok + isSet = isSet || ok } - return isSetted, nil + return isSet, 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) } -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] if !ok && !opt.isDefaultExists { return false, nil diff --git a/binding/multipart_form_mapping.go b/binding/multipart_form_mapping.go index 69c0a5443d..c4d7ed7421 100644 --- a/binding/multipart_form_mapping.go +++ b/binding/multipart_form_mapping.go @@ -32,7 +32,7 @@ func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField 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() { case reflect.Ptr: switch value.Interface().(type) { @@ -48,9 +48,9 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file } case reflect.Slice: slice := reflect.MakeSlice(value.Type(), len(files), len(files)) - isSetted, err = setArrayOfMultipartFormFiles(slice, field, files) - if err != nil || !isSetted { - return isSetted, err + isSet, err = setArrayOfMultipartFormFiles(slice, field, files) + if err != nil || !isSet { + return isSet, err } value.Set(slice) return true, nil @@ -60,14 +60,14 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file 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) { return false, ErrMultiFileHeaderLenInvalid } for i := range files { - setted, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1]) - if err != nil || !setted { - return setted, err + set, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1]) + if err != nil || !set { + return set, err } } return true, nil diff --git a/errors_test.go b/errors_test.go index ee95ab31ec..9a17d8594e 100644 --- a/errors_test.go +++ b/errors_test.go @@ -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()". // "errors.Is()" and "errors.As()" have been added to the standard library in go 1.13. 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 err := fmt.Errorf("wrapped: %w", &Error{ From 39181329dee3a14fa2210c72245811c07d5e72b6 Mon Sep 17 00:00:00 2001 From: Notealot <714804968@qq.com> Date: Wed, 6 Oct 2021 09:37:25 +0800 Subject: [PATCH 047/346] Tidy: Complete TrustedProxies feature (#2887) --- README.md | 12 +++++-- context.go | 2 +- context_test.go | 4 +++ gin.go | 70 +++++++++++++++++++++++------------------ gin_integration_test.go | 7 +++++ gin_test.go | 57 +++++++++++---------------------- 6 files changed, 80 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 10be5e155d..62094ff101 100644 --- a/README.md +++ b/README.md @@ -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 specify one of these headers. -The `TrustedProxies` slice on your `gin.Engine` specifes network addresses or -network CIDRs from where clients which their request headers related to client +Use function `SetTrustedProxies()` on your `gin.Engine` to specify network addresses +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 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 import ( "fmt" @@ -2217,7 +2223,7 @@ import ( func main() { router := gin.Default() - router.TrustedProxies = []string{"192.168.1.2"} + router.SetTrustedProxies([]string{"192.168.1.2"}) router.GET("/", func(c *gin.Context) { // If the client is 192.168.1.2, use the X-Forwarded-For diff --git a/context.go b/context.go index 370f608634..d211098b72 100644 --- a/context.go +++ b/context.go @@ -778,7 +778,7 @@ func (c *Context) ClientIP() string { // 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 in Engine.TrustedProxies +// defined by Engine.SetTrustedProxies() func (c *Context) RemoteIP() (net.IP, bool) { ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)) if err != nil { diff --git a/context_test.go b/context_test.go index ad16fbb1c6..07a35f523b 100644 --- a/context_test.go +++ b/context_test.go @@ -1409,6 +1409,10 @@ func TestContextClientIP(t *testing.T) { c.engine.RemoteIPHeaders = []string{"X-Forwarded-For"} 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 _ = c.engine.SetTrustedProxies([]string{"30.30.30.30"}) assert.Equal(t, "40.40.40.40", c.ClientIP()) diff --git a/gin.go b/gin.go index 02a1062c31..af83161b68 100644 --- a/gin.go +++ b/gin.go @@ -11,6 +11,7 @@ import ( "net/http" "os" "path" + "reflect" "strings" "sync" @@ -27,6 +28,8 @@ 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 + // HandlerFunc defines the handler used by gin middleware as return value. type HandlerFunc func(*Context) @@ -119,15 +122,9 @@ type Engine struct { // List of headers used to obtain the client IP when // `(*gin.Engine).ForwardedByClientIP` is `true` and // `(*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 - // 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 // that platform, for example to determine the client IP TrustedPlatform string @@ -147,6 +144,7 @@ type Engine struct { pool sync.Pool trees methodTrees maxParams uint16 + trustedProxies []string trustedCIDRs []*net.IPNet } @@ -174,7 +172,6 @@ func New() *Engine { HandleMethodNotAllowed: false, ForwardedByClientIP: true, RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"}, - TrustedProxies: []string{"0.0.0.0/0"}, TrustedPlatform: defaultPlatform, UseRawPath: false, RemoveExtraSlash: false, @@ -183,7 +180,8 @@ func New() *Engine { trees: make(methodTrees, 0, 9), delims: render.Delims{Left: "{{", Right: "}}"}, secureJSONPrefix: "while(1);", - trustedCIDRs: []*net.IPNet{{IP: net.IP{0x0, 0x0, 0x0, 0x0}, Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}}}, + trustedProxies: []string{"0.0.0.0/0"}, + trustedCIDRs: defaultTrustedCIDRs, } engine.RouterGroup.engine = engine engine.pool.New = func() interface{} { @@ -342,9 +340,9 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo { func (engine *Engine) Run(addr ...string) (err error) { defer func() { debugPrintError(err) }() - err = engine.parseTrustedProxies() - if err != nil { - return err + if engine.isUnsafeTrustedProxies() { + debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") } address := resolveAddress(addr) @@ -354,12 +352,12 @@ func (engine *Engine) Run(addr ...string) (err error) { } func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) { - if engine.TrustedProxies == nil { + if engine.trustedProxies == nil { return nil, nil } - cidr := make([]*net.IPNet, 0, len(engine.TrustedProxies)) - for _, trustedProxy := range engine.TrustedProxies { + cidr := make([]*net.IPNet, 0, len(engine.trustedProxies)) + for _, trustedProxy := range engine.trustedProxies { if !strings.Contains(trustedProxy, "/") { ip := parseIP(trustedProxy) if ip == nil { @@ -382,13 +380,25 @@ func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) { 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 { - engine.TrustedProxies = trustedProxies + engine.trustedProxies = trustedProxies 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 { trustedCIDRs, err := engine.prepareTrustedCIDRs() engine.trustedCIDRs = trustedCIDRs @@ -416,9 +426,9 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) { debugPrint("Listening and serving HTTPS on %s\n", addr) defer func() { debugPrintError(err) }() - err = engine.parseTrustedProxies() - if err != nil { - return err + if engine.isUnsafeTrustedProxies() { + debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + "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) @@ -432,9 +442,9 @@ func (engine *Engine) RunUnix(file string) (err error) { debugPrint("Listening and serving HTTP on unix:/%s", file) defer func() { debugPrintError(err) }() - err = engine.parseTrustedProxies() - if err != nil { - return err + if engine.isUnsafeTrustedProxies() { + debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + "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) @@ -455,9 +465,9 @@ func (engine *Engine) RunFd(fd int) (err error) { debugPrint("Listening and serving HTTP on fd@%d", fd) defer func() { debugPrintError(err) }() - err = engine.parseTrustedProxies() - if err != nil { - return err + if engine.isUnsafeTrustedProxies() { + debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + "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)) @@ -476,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()) defer func() { debugPrintError(err) }() - err = engine.parseTrustedProxies() - if err != nil { - return err + if engine.isUnsafeTrustedProxies() { + debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") } err = http.Serve(listener, engine) diff --git a/gin_integration_test.go b/gin_integration_test.go index 094c46e871..0b67b542de 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -76,6 +76,12 @@ func TestRunEmpty(t *testing.T) { 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) { os.Setenv("PORT", "") router := New() @@ -143,6 +149,7 @@ func TestBadTrustedCIDRsForRunTLS(t *testing.T) { router.TrustedProxies = []string{"hello/world"} assert.Error(t, router.RunTLS(":8080", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) } +*/ func TestRunTLS(t *testing.T) { router := New() diff --git a/gin_test.go b/gin_test.go index 678d95f2c8..21c43d1520 100644 --- a/gin_test.go +++ b/gin_test.go @@ -539,19 +539,15 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { // valid ipv4 cidr { expectedTrustedCIDRs := []*net.IPNet{parseCIDR("0.0.0.0/0")} - r.TrustedProxies = []string{"0.0.0.0/0"} - - trustedCIDRs, err := r.prepareTrustedCIDRs() + err := r.SetTrustedProxies([]string{"0.0.0.0/0"}) assert.NoError(t, err) - assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs) + assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } // invalid ipv4 cidr { - r.TrustedProxies = []string{"192.168.1.33/33"} - - _, err := r.prepareTrustedCIDRs() + err := r.SetTrustedProxies([]string{"192.168.1.33/33"}) assert.Error(t, err) } @@ -559,19 +555,16 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { // valid ipv4 address { 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.Equal(t, expectedTrustedCIDRs, trustedCIDRs) + assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } // invalid ipv4 address { - r.TrustedProxies = []string{"192.168.1.256"} - - _, err := r.prepareTrustedCIDRs() + err := r.SetTrustedProxies([]string{"192.168.1.256"}) assert.Error(t, err) } @@ -579,19 +572,15 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { // valid ipv6 address { expectedTrustedCIDRs := []*net.IPNet{parseCIDR("2002:0000:0000:1234:abcd:ffff:c0a8:0101/128")} - r.TrustedProxies = []string{"2002:0000:0000:1234:abcd:ffff:c0a8:0101"} - - trustedCIDRs, err := r.prepareTrustedCIDRs() + err := r.SetTrustedProxies([]string{"2002:0000:0000:1234:abcd:ffff:c0a8:0101"}) assert.NoError(t, err) - assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs) + assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } // invalid ipv6 address { - r.TrustedProxies = []string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101"} - - _, err := r.prepareTrustedCIDRs() + err := r.SetTrustedProxies([]string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101"}) assert.Error(t, err) } @@ -599,19 +588,15 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { // valid ipv6 cidr { expectedTrustedCIDRs := []*net.IPNet{parseCIDR("::/0")} - r.TrustedProxies = []string{"::/0"} - - trustedCIDRs, err := r.prepareTrustedCIDRs() + err := r.SetTrustedProxies([]string{"::/0"}) assert.NoError(t, err) - assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs) + assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } // invalid ipv6 cidr { - r.TrustedProxies = []string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101/129"} - - _, err := r.prepareTrustedCIDRs() + err := r.SetTrustedProxies([]string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101/129"}) assert.Error(t, err) } @@ -623,36 +608,32 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { parseCIDR("192.168.0.0/16"), parseCIDR("172.16.0.1/32"), } - r.TrustedProxies = []string{ + err := r.SetTrustedProxies([]string{ "::/0", "192.168.0.0/16", "172.16.0.1", - } - - trustedCIDRs, err := r.prepareTrustedCIDRs() + }) assert.NoError(t, err) - assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs) + assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } // invalid combination { - r.TrustedProxies = []string{ + err := r.SetTrustedProxies([]string{ "::/0", "192.168.0.0/16", "172.16.0.256", - } - _, err := r.prepareTrustedCIDRs() + }) assert.Error(t, err) } // nil value { - r.TrustedProxies = nil - trustedCIDRs, err := r.prepareTrustedCIDRs() + err := r.SetTrustedProxies(nil) - assert.Nil(t, trustedCIDRs) + assert.Nil(t, r.trustedCIDRs) assert.Nil(t, err) } From 21125bbb3f550dbfa4c64151f7e01f58dd64e2b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Oct 2021 09:38:42 +0800 Subject: [PATCH 048/346] Bump github.com/goccy/go-json from 0.7.8 to 0.7.9 (#2891) 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 80807e85be..c03439a018 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.8 + github.com/goccy/go-json v0.7.9 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 33ce8cd709..7bf71f9ffe 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.8 h1:CvMH7LotYymYuLGEohBM1lTZWX4g6jzWUUl2aLFuBoE= -github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.7.9 h1:mSp3uo1tr6MXQTYopSNhHTUnJhd2zQ4Yk+HdJZP+ZRY= +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/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 5929d521715610c9dd14898ebbe1d188d5de8937 Mon Sep 17 00:00:00 2001 From: Egor Seredin <4819888+agmt@users.noreply.github.com> Date: Sat, 9 Oct 2021 09:38:51 +0900 Subject: [PATCH 049/346] ClientIP: check every proxy for trustiness (#2844) --- context.go | 39 ++++++++++++++++++++------------------- context_test.go | 2 +- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/context.go b/context.go index d211098b72..bea95ccaba 100644 --- a/context.go +++ b/context.go @@ -766,7 +766,7 @@ func (c *Context) ClientIP() string { if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil { for _, headerName := range c.engine.RemoteIPHeaders { - ip, valid := validateHeader(c.requestHeader(headerName)) + ip, valid := c.engine.validateHeader(c.requestHeader(headerName)) if valid { return ip } @@ -775,6 +775,17 @@ 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 @@ -789,35 +800,25 @@ func (c *Context) RemoteIP() (net.IP, bool) { return nil, false } - if c.engine.trustedCIDRs != nil { - for _, cidr := range c.engine.trustedCIDRs { - if cidr.Contains(remoteIP) { - return remoteIP, true - } - } - } - - return remoteIP, false + return remoteIP, c.engine.isTrustedProxy(remoteIP) } -func validateHeader(header string) (clientIP string, valid bool) { +func (e *Engine) validateHeader(header string) (clientIP string, valid bool) { if header == "" { return "", false } items := strings.Split(header, ",") - for i, ipStr := range items { - ipStr = strings.TrimSpace(ipStr) + for i := len(items) - 1; i >= 0; i-- { + ipStr := strings.TrimSpace(items[i]) ip := net.ParseIP(ipStr) if ip == nil { return "", false } - // We need to return the first IP in the list, but, - // we should not early return since we need to validate that - // the rest of the header is syntactically valid - if i == 0 { - clientIP = ipStr - valid = true + // 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 diff --git a/context_test.go b/context_test.go index 07a35f523b..e9fe88f920 100644 --- a/context_test.go +++ b/context_test.go @@ -1419,7 +1419,7 @@ func TestContextClientIP(t *testing.T) { // Only trust RemoteAddr _ = c.engine.SetTrustedProxies([]string{"40.40.40.40"}) - assert.Equal(t, "20.20.20.20", c.ClientIP()) + assert.Equal(t, "30.30.30.30", c.ClientIP()) // All steps are trusted _ = c.engine.SetTrustedProxies([]string{"40.40.40.40", "30.30.30.30", "20.20.20.20"}) From 464535ff94799b012f90f15edcffd32e4abe32fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Oct 2021 07:55:14 +0800 Subject: [PATCH 050/346] Bump github.com/goccy/go-json from 0.7.9 to 0.7.10 (#2905) 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 c03439a018..c25eecf9df 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.9 + github.com/goccy/go-json v0.7.10 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 7bf71f9ffe..51497a0bac 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.9 h1:mSp3uo1tr6MXQTYopSNhHTUnJhd2zQ4Yk+HdJZP+ZRY= -github.com/goccy/go-json v0.7.9/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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/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 34ae7777cada2ab0f0ca650363fef7134d7ad74f Mon Sep 17 00:00:00 2001 From: Allen Ren Date: Fri, 22 Oct 2021 21:26:14 +0800 Subject: [PATCH 051/346] README.md: fix a typo (#2913) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62094ff101..0fa78ecaba 100644 --- a/README.md +++ b/README.md @@ -2000,7 +2000,7 @@ func SomeHandler(c *gin.Context) { objA := formA{} objB := formB{} // This reads c.Request.Body and stores the result into the context. - if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil { + if errA := c.ShouldBindBodyWith(&objA, binding.Form); errA == nil { c.String(http.StatusOK, `the body should be formA`) // At this time, it reuses body stored in the context. } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil { From 3fe928994b7ba7ed16b0c5114ca82a9dafa7a974 Mon Sep 17 00:00:00 2001 From: Zhu Xi Date: Sat, 23 Oct 2021 11:58:57 +0800 Subject: [PATCH 052/346] Update the code logic for latestNode in tree.go (#2897) --- context.go | 6 ++- gin.go | 12 +++-- gin_integration_test.go | 15 ++++++ tree.go | 117 ++++++++++++++++++++++++++-------------- tree_test.go | 22 +++++--- 5 files changed, 120 insertions(+), 52 deletions(-) diff --git a/context.go b/context.go index bea95ccaba..bc2c38e1d8 100644 --- a/context.go +++ b/context.go @@ -55,8 +55,9 @@ type Context struct { index int8 fullPath string - engine *Engine - params *Params + engine *Engine + params *Params + skippedNodes *[]skippedNode // This mutex protect Keys map mu sync.RWMutex @@ -99,6 +100,7 @@ func (c *Context) reset() { c.queryCache = nil c.formCache = nil *c.params = (*c.params)[:0] + *c.skippedNodes = (*c.skippedNodes)[:0] } // Copy returns a copy of the current context that can be safely used outside the request's scope. diff --git a/gin.go b/gin.go index af83161b68..1774717f83 100644 --- a/gin.go +++ b/gin.go @@ -144,6 +144,7 @@ type Engine struct { pool sync.Pool trees methodTrees maxParams uint16 + maxSections uint16 trustedProxies []string trustedCIDRs []*net.IPNet } @@ -200,7 +201,8 @@ func Default() *Engine { func (engine *Engine) allocateContext() *Context { v := make(Params, 0, engine.maxParams) - return &Context{engine: engine, params: &v} + skippedNodes := make([]skippedNode, 0, engine.maxSections) + return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes} } // Delims sets template left and right delims and returns a Engine instance. @@ -306,6 +308,10 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { if paramsCount := countParams(path); paramsCount > engine.maxParams { engine.maxParams = paramsCount } + + if sectionsCount := countSections(path); sectionsCount > engine.maxSections { + engine.maxSections = sectionsCount + } } // Routes returns a slice of registered routes, including some useful information, such as: @@ -539,7 +545,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) { } root := t[i].root // Find route in tree - value := root.getValue(rPath, c.params, unescape) + value := root.getValue(rPath, c.params, c.skippedNodes, unescape) if value.params != nil { c.Params = *value.params } @@ -567,7 +573,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) { if tree.method == httpMethod { continue } - if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil { + if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil { c.handlers = engine.allNoMethod serveError(c, http.StatusMethodNotAllowed, default405Body) return diff --git a/gin_integration_test.go b/gin_integration_test.go index 0b67b542de..8c22e7bdad 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -408,8 +408,13 @@ func TestTreeRunDynamicRouting(t *testing.T) { router.GET("/ab/*xx", func(c *Context) { c.String(http.StatusOK, "/ab/*xx") }) router.GET("/", func(c *Context) { c.String(http.StatusOK, "home") }) router.GET("/:cc", func(c *Context) { c.String(http.StatusOK, "/:cc") }) + router.GET("/c1/:dd/e", func(c *Context) { c.String(http.StatusOK, "/c1/:dd/e") }) + router.GET("/c1/:dd/e1", func(c *Context) { c.String(http.StatusOK, "/c1/:dd/e1") }) + router.GET("/c1/:dd/f1", func(c *Context) { c.String(http.StatusOK, "/c1/:dd/f1") }) + router.GET("/c1/:dd/f2", func(c *Context) { c.String(http.StatusOK, "/c1/:dd/f2") }) router.GET("/:cc/cc", func(c *Context) { c.String(http.StatusOK, "/:cc/cc") }) router.GET("/:cc/:dd/ee", func(c *Context) { c.String(http.StatusOK, "/:cc/:dd/ee") }) + router.GET("/:cc/:dd/f", func(c *Context) { c.String(http.StatusOK, "/:cc/:dd/f") }) router.GET("/:cc/:dd/:ee/ff", func(c *Context) { c.String(http.StatusOK, "/:cc/:dd/:ee/ff") }) router.GET("/:cc/:dd/:ee/:ff/gg", func(c *Context) { c.String(http.StatusOK, "/:cc/:dd/:ee/:ff/gg") }) router.GET("/:cc/:dd/:ee/:ff/:gg/hh", func(c *Context) { c.String(http.StatusOK, "/:cc/:dd/:ee/:ff/:gg/hh") }) @@ -446,6 +451,10 @@ func TestTreeRunDynamicRouting(t *testing.T) { testRequest(t, ts.URL+"/all", "", "/:cc") testRequest(t, ts.URL+"/all/cc", "", "/:cc/cc") testRequest(t, ts.URL+"/a/cc", "", "/:cc/cc") + testRequest(t, ts.URL+"/c1/d/e", "", "/c1/:dd/e") + testRequest(t, ts.URL+"/c1/d/e1", "", "/c1/:dd/e1") + testRequest(t, ts.URL+"/c1/d/ee", "", "/:cc/:dd/ee") + testRequest(t, ts.URL+"/c1/d/f", "", "/:cc/:dd/f") testRequest(t, ts.URL+"/c/d/ee", "", "/:cc/:dd/ee") testRequest(t, ts.URL+"/c/d/e/ff", "", "/:cc/:dd/:ee/ff") testRequest(t, ts.URL+"/c/d/e/f/gg", "", "/:cc/:dd/:ee/:ff/gg") @@ -528,6 +537,12 @@ func TestTreeRunDynamicRouting(t *testing.T) { testRequest(t, ts.URL+"/get/abc/123abf/testss", "", "/get/abc/123abf/:param") testRequest(t, ts.URL+"/get/abc/123abfff/te", "", "/get/abc/123abfff/:param") // 404 not found + testRequest(t, ts.URL+"/c/d/e", "404 Not Found") + testRequest(t, ts.URL+"/c/d/e1", "404 Not Found") + testRequest(t, ts.URL+"/c/d/eee", "404 Not Found") + testRequest(t, ts.URL+"/c1/d/eee", "404 Not Found") + testRequest(t, ts.URL+"/c1/d/e2", "404 Not Found") + testRequest(t, ts.URL+"/cc/dd/ee/ff/gg/hh1", "404 Not Found") testRequest(t, ts.URL+"/a/dd", "404 Not Found") testRequest(t, ts.URL+"/addr/dd/aa", "404 Not Found") testRequest(t, ts.URL+"/something/secondthing/121", "404 Not Found") diff --git a/tree.go b/tree.go index fb0a5935c2..c8a7548e4f 100644 --- a/tree.go +++ b/tree.go @@ -17,6 +17,7 @@ import ( var ( strColon = []byte(":") strStar = []byte("*") + strSlash = []byte("/") ) // Param is a single URL parameter, consisting of a key and a value. @@ -98,6 +99,11 @@ func countParams(path string) uint16 { return n } +func countSections(path string) uint16 { + s := bytesconv.StringToBytes(path) + return uint16(bytes.Count(s, strSlash)) +} + type nodeType uint8 const ( @@ -393,16 +399,19 @@ type nodeValue struct { fullPath string } +type skippedNode struct { + path string + node *node + paramsCount int16 +} + // Returns the handle registered with the given path (key). The values of // wildcards are saved to a map. // If no handle can be found, a TSR (trailing slash redirect) recommendation is // made if a handle exists with an extra (without the) trailing slash for the // given path. -func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) { - var ( - skippedPath string - latestNode = n // Caching the latest node - ) +func (n *node) getValue(path string, params *Params, skippedNodes *[]skippedNode, unescape bool) (value nodeValue) { + var globalParamsCount int16 walk: // Outer loop for walking the tree for { @@ -417,15 +426,20 @@ walk: // Outer loop for walking the tree if c == idxc { // strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild if n.wildChild { - skippedPath = prefix + path - latestNode = &node{ - path: n.path, - wildChild: n.wildChild, - nType: n.nType, - priority: n.priority, - children: n.children, - handlers: n.handlers, - fullPath: n.fullPath, + index := len(*skippedNodes) + *skippedNodes = (*skippedNodes)[:index+1] + (*skippedNodes)[index] = skippedNode{ + path: prefix + path, + node: &node{ + path: n.path, + wildChild: n.wildChild, + nType: n.nType, + priority: n.priority, + children: n.children, + handlers: n.handlers, + fullPath: n.fullPath, + }, + paramsCount: globalParamsCount, } } @@ -434,10 +448,22 @@ walk: // Outer loop for walking the tree } } // If the path at the end of the loop is not equal to '/' and the current node has no child nodes - // the current node needs to be equal to the latest matching node - matched := path != "/" && !n.wildChild - if matched { - n = latestNode + // the current node needs to roll back to last vaild skippedNode + + if path != "/" && !n.wildChild { + for l := len(*skippedNodes); l > 0; { + skippedNode := (*skippedNodes)[l-1] + *skippedNodes = (*skippedNodes)[:l-1] + if strings.HasSuffix(skippedNode.path, path) { + path = skippedNode.path + n = skippedNode.node + if value.params != nil { + *value.params = (*value.params)[:skippedNode.paramsCount] + } + globalParamsCount = skippedNode.paramsCount + continue walk + } + } } // If there is no wildcard pattern, recommend a redirection @@ -451,18 +477,12 @@ walk: // Outer loop for walking the tree // Handle wildcard child, which is always at the end of the array n = n.children[len(n.children)-1] + globalParamsCount++ switch n.nType { case param: // fix truncate the parameter // tree_test.go line: 204 - if matched { - path = prefix + path - // The saved path is used after the prefix route is intercepted by matching - if n.indices == "/" { - path = skippedPath[1:] - } - } // Find param end (either '/' or path end) end := 0 @@ -548,9 +568,22 @@ walk: // Outer loop for walking the tree if path == prefix { // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node - // the current node needs to be equal to the latest matching node - if latestNode.wildChild && n.handlers == nil && path != "/" { - n = latestNode.children[len(latestNode.children)-1] + // the current node needs to roll back to last vaild skippedNode + if n.handlers == nil && path != "/" { + for l := len(*skippedNodes); l > 0; { + skippedNode := (*skippedNodes)[l-1] + *skippedNodes = (*skippedNodes)[:l-1] + if strings.HasSuffix(skippedNode.path, path) { + path = skippedNode.path + n = skippedNode.node + if value.params != nil { + *value.params = (*value.params)[:skippedNode.paramsCount] + } + globalParamsCount = skippedNode.paramsCount + continue walk + } + } + // n = latestNode.children[len(latestNode.children)-1] } // We should have reached the node containing the handle. // Check if this node has a handle registered. @@ -581,19 +614,21 @@ walk: // Outer loop for walking the tree return } - if path != "/" && len(skippedPath) > 0 && strings.HasSuffix(skippedPath, path) { - path = skippedPath - // Reduce the number of cycles - n, latestNode = latestNode, n - // skippedPath cannot execute - // example: - // * /:cc/cc - // call /a/cc expectations:match/200 Actual:match/200 - // call /a/dd expectations:unmatch/404 Actual: panic - // call /addr/dd/aa expectations:unmatch/404 Actual: panic - // skippedPath: It can only be executed if the secondary route is not found - skippedPath = "" - continue walk + // roll back to last vaild skippedNode + if path != "/" { + for l := len(*skippedNodes); l > 0; { + skippedNode := (*skippedNodes)[l-1] + *skippedNodes = (*skippedNodes)[:l-1] + if strings.HasSuffix(skippedNode.path, path) { + path = skippedNode.path + n = skippedNode.node + if value.params != nil { + *value.params = (*value.params)[:skippedNode.paramsCount] + } + globalParamsCount = skippedNode.paramsCount + continue walk + } + } } // Nothing found. We can recommend to redirect to the same URL with an diff --git a/tree_test.go b/tree_test.go index 8ae5b7db65..49b3b57ef6 100644 --- a/tree_test.go +++ b/tree_test.go @@ -33,6 +33,11 @@ func getParams() *Params { return &ps } +func getSkippedNodes() *[]skippedNode { + ps := make([]skippedNode, 0, 20) + return &ps +} + func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ...bool) { unescape := false if len(unescapes) >= 1 { @@ -40,7 +45,7 @@ func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes .. } for _, request := range requests { - value := tree.getValue(request.path, getParams(), unescape) + value := tree.getValue(request.path, getParams(), getSkippedNodes(), unescape) if value.handlers == nil { if !request.nilHandler { @@ -157,6 +162,8 @@ func TestTreeWildcard(t *testing.T) { "/aa/*xx", "/ab/*xx", "/:cc", + "/c1/:dd/e", + "/c1/:dd/e1", "/:cc/cc", "/:cc/:dd/ee", "/:cc/:dd/:ee/ff", @@ -238,6 +245,9 @@ func TestTreeWildcard(t *testing.T) { {"/alldd", false, "/:cc", Params{Param{Key: "cc", Value: "alldd"}}}, {"/all/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "all"}}}, {"/a/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "a"}}}, + {"/c1/d/e", false, "/c1/:dd/e", Params{Param{Key: "dd", Value: "d"}}}, + {"/c1/d/e1", false, "/c1/:dd/e1", Params{Param{Key: "dd", Value: "d"}}}, + {"/c1/d/ee", false, "/:cc/:dd/ee", Params{Param{Key: "cc", Value: "c1"}, Param{Key: "dd", Value: "d"}}}, {"/cc/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "cc"}}}, {"/ccc/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "ccc"}}}, {"/deedwjfs/cc", false, "/:cc/cc", Params{Param{Key: "cc", Value: "deedwjfs"}}}, @@ -605,7 +615,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) { "/doc/", } for _, route := range tsrRoutes { - value := tree.getValue(route, nil, false) + value := tree.getValue(route, nil, getSkippedNodes(), false) if value.handlers != nil { t.Fatalf("non-nil handler for TSR route '%s", route) } else if !value.tsr { @@ -622,7 +632,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) { "/api/world/abc", } for _, route := range noTsrRoutes { - value := tree.getValue(route, nil, false) + value := tree.getValue(route, nil, getSkippedNodes(), false) if value.handlers != nil { t.Fatalf("non-nil handler for No-TSR route '%s", route) } else if value.tsr { @@ -641,7 +651,7 @@ func TestTreeRootTrailingSlashRedirect(t *testing.T) { t.Fatalf("panic inserting test route: %v", recv) } - value := tree.getValue("/", nil, false) + value := tree.getValue("/", nil, getSkippedNodes(), false) if value.handlers != nil { t.Fatalf("non-nil handler") } else if value.tsr { @@ -821,7 +831,7 @@ func TestTreeInvalidNodeType(t *testing.T) { // normal lookup recv := catchPanic(func() { - tree.getValue("/test", nil, false) + tree.getValue("/test", nil, getSkippedNodes(), false) }) if rs, ok := recv.(string); !ok || rs != panicMsg { t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv) @@ -846,7 +856,7 @@ func TestTreeInvalidParamsType(t *testing.T) { params := make(Params, 0) // try to trigger slice bounds out of range with capacity 0 - tree.getValue("/test", ¶ms, false) + tree.getValue("/test", ¶ms, getSkippedNodes(), false) } func TestTreeWildcardConflictEx(t *testing.T) { From 2d3d6d2f1355f21a77f8ef6cf45a5bc531c8c935 Mon Sep 17 00:00:00 2001 From: Notealot <714804968@qq.com> Date: Sun, 24 Oct 2021 08:34:03 +0800 Subject: [PATCH 053/346] Provide custom options of TrustedPlatform for another CDN services (#2906) * refine TrustedPlatform and docs * refactor for switch * refactor switch to if statement --- README.md | 29 +++++++++++++++++++++++++++++ context.go | 14 +++++--------- context_test.go | 14 +++++++++++++- gin.go | 4 ++-- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0fa78ecaba..cad746d62c 100644 --- a/README.md +++ b/README.md @@ -78,6 +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) - [Testing](#testing) - [Users](#users) @@ -2236,6 +2237,34 @@ func main() { } ``` +**Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform` +to skip TrustedProxies check, it has a higher priority than TrustedProxies. +Look at the example below: +```go +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +func main() { + + router := gin.Default() + // Use predefined header gin.PlatformXXX + router.TrustedPlatform = gin.PlatformGoogleAppEngine + // Or set your own trusted request header for another trusted proxy service + // Don't set it to any suspect request header, it's unsafe + router.TrustedPlatform = "X-CDN-IP" + + router.GET("/", func(c *gin.Context) { + // If you set TrustedPlatform, ClientIP() will resolve the + // corresponding header and return IP directly + fmt.Printf("ClientIP: %s\n", c.ClientIP()) + }) + router.Run() +} +``` + ## Testing The `net/http/httptest` package is preferable way for HTTP testing. diff --git a/context.go b/context.go index bc2c38e1d8..58f38c88cb 100644 --- a/context.go +++ b/context.go @@ -735,20 +735,16 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e return bb.BindBody(body, obj) } -// ClientIP implements a best effort algorithm to return the real client IP. +// ClientIP implements one 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. // 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. func (c *Context) ClientIP() string { - // Check if we're running on a trusted platform - switch c.engine.TrustedPlatform { - case PlatformGoogleAppEngine: - if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { - return addr - } - case PlatformCloudflare: - if addr := c.requestHeader("CF-Connecting-IP"); addr != "" { + // Check if we're running on a trusted platform, continue running backwards if error + if c.engine.TrustedPlatform != "" { + // Developers can define their own header of Trusted Platform or use predefined constants + if addr := c.requestHeader(c.engine.TrustedPlatform); addr != "" { return addr } } diff --git a/context_test.go b/context_test.go index e9fe88f920..c286c0f4cb 100644 --- a/context_test.go +++ b/context_test.go @@ -1458,8 +1458,20 @@ func TestContextClientIP(t *testing.T) { c.engine.TrustedPlatform = PlatformGoogleAppEngine assert.Equal(t, "50.50.50.50", c.ClientIP()) - // Test the legacy flag + // Use custom TrustedPlatform header + c.engine.TrustedPlatform = "X-CDN-IP" + c.Request.Header.Set("X-CDN-IP", "80.80.80.80") + assert.Equal(t, "80.80.80.80", c.ClientIP()) + // wrong header + c.engine.TrustedPlatform = "X-Wrong-Header" + assert.Equal(t, "40.40.40.40", c.ClientIP()) + + c.Request.Header.Del("X-CDN-IP") + // TrustedPlatform is empty c.engine.TrustedPlatform = "" + assert.Equal(t, "40.40.40.40", c.ClientIP()) + + // Test the legacy flag c.engine.AppEngine = true assert.Equal(t, "50.50.50.50", c.ClientIP()) c.engine.AppEngine = false diff --git a/gin.go b/gin.go index 1774717f83..1d9c9fee44 100644 --- a/gin.go +++ b/gin.go @@ -59,10 +59,10 @@ type RoutesInfo []RouteInfo const ( // When running on Google App Engine. Trust X-Appengine-Remote-Addr // for determining the client's IP - PlatformGoogleAppEngine = "google-app-engine" + PlatformGoogleAppEngine = "X-Appengine-Remote-Addr" // When using Cloudflare's CDN. Trust CF-Connecting-IP for determining // the client's IP - PlatformCloudflare = "cloudflare" + PlatformCloudflare = "CF-Connecting-IP" ) // Engine is the framework's instance, it contains the muxer, middleware and configuration settings. From eb75ce0ff5d64b269f2a25e83952e374e762c4b6 Mon Sep 17 00:00:00 2001 From: heige Date: Sun, 24 Oct 2021 09:31:13 +0800 Subject: [PATCH 054/346] adjust the routergroup Any method (#2701) --- routergroup.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/routergroup.go b/routergroup.go index bb24bd5232..27d7aad671 100644 --- a/routergroup.go +++ b/routergroup.go @@ -14,6 +14,13 @@ import ( var ( // reg match english letters for http method name regEnLetter = regexp.MustCompile("^[A-Z]+$") + + // anyMethods for RouterGroup Any method + anyMethods = []string{ + http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, + http.MethodHead, http.MethodOptions, http.MethodDelete, http.MethodConnect, + http.MethodTrace, + } ) // IRouter defines all router handle interface includes single and group router. @@ -136,15 +143,10 @@ func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRo // Any registers a route that matches all the HTTP methods. // GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE. func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes { - group.handle(http.MethodGet, relativePath, handlers) - group.handle(http.MethodPost, relativePath, handlers) - group.handle(http.MethodPut, relativePath, handlers) - group.handle(http.MethodPatch, relativePath, handlers) - group.handle(http.MethodHead, relativePath, handlers) - group.handle(http.MethodOptions, relativePath, handlers) - group.handle(http.MethodDelete, relativePath, handlers) - group.handle(http.MethodConnect, relativePath, handlers) - group.handle(http.MethodTrace, relativePath, handlers) + for _, method := range anyMethods { + group.handle(method, relativePath, handlers) + } + return group.returnObj() } From 1c2aa59b20c8cff5b3c601708afe22100eac25e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B8=82=E6=B0=91233?= Date: Tue, 26 Oct 2021 18:15:29 +0800 Subject: [PATCH 055/346] fix the misplacement of adding slashes (#2847) --- routes_test.go | 15 +++++++++++++++ tree.go | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/routes_test.go b/routes_test.go index 036fa1c349..ffe3469e7c 100644 --- a/routes_test.go +++ b/routes_test.go @@ -481,6 +481,21 @@ func TestRouterNotFound(t *testing.T) { router.GET("/a", func(c *Context) {}) w = performRequest(router, http.MethodGet, "/") assert.Equal(t, http.StatusNotFound, w.Code) + + // Reproduction test for the bug of issue #2843 + router = New() + router.NoRoute(func(c *Context) { + if c.Request.RequestURI == "/login" { + c.String(200, "login") + } + }) + router.GET("/logout", func(c *Context) { + c.String(200, "logout") + }) + w = performRequest(router, http.MethodGet, "/login") + assert.Equal(t, "login", w.Body.String()) + w = performRequest(router, http.MethodGet, "/logout") + assert.Equal(t, "logout", w.Body.String()) } func TestRouterStaticFSNotFound(t *testing.T) { diff --git a/tree.go b/tree.go index c8a7548e4f..a0d9835209 100644 --- a/tree.go +++ b/tree.go @@ -634,7 +634,8 @@ walk: // Outer loop for walking the tree // Nothing found. We can recommend to redirect to the same URL with an // extra trailing slash if a leaf exists for that path value.tsr = path == "/" || - (len(prefix) == len(path)+1 && n.handlers != nil) + (len(prefix) == len(path)+1 && prefix[len(path)] == '/' && + path == prefix[:len(prefix)-1] && n.handlers != nil) return } } From d4e72a17f7771d929a35bc8bb561ee24676dac1a Mon Sep 17 00:00:00 2001 From: Tommy Chu Date: Sun, 31 Oct 2021 02:23:08 +0100 Subject: [PATCH 056/346] Fix typo (#2926) --- errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors.go b/errors.go index 0f276c13d4..3418cbc806 100644 --- a/errors.go +++ b/errors.go @@ -122,7 +122,7 @@ func (a errorMsgs) Last() *Error { return nil } -// Errors returns an array will all the error messages. +// Errors returns an array with all the error messages. // Example: // c.Error(errors.New("first")) // c.Error(errors.New("second")) From cbdd47a7e108217d4672f0e24d423b632a8cd900 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Sun, 31 Oct 2021 20:21:37 -0400 Subject: [PATCH 057/346] fix tsr with mixed static and wildcard paths (#2924) --- tree.go | 48 ++++++++++++++++++++++++------------------------ tree_test.go | 27 +++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/tree.go b/tree.go index a0d9835209..2f0de1a96e 100644 --- a/tree.go +++ b/tree.go @@ -447,27 +447,26 @@ walk: // Outer loop for walking the tree continue walk } } - // If the path at the end of the loop is not equal to '/' and the current node has no child nodes - // the current node needs to roll back to last vaild skippedNode - - if path != "/" && !n.wildChild { - for l := len(*skippedNodes); l > 0; { - skippedNode := (*skippedNodes)[l-1] - *skippedNodes = (*skippedNodes)[:l-1] - if strings.HasSuffix(skippedNode.path, path) { - path = skippedNode.path - n = skippedNode.node - if value.params != nil { - *value.params = (*value.params)[:skippedNode.paramsCount] + + if !n.wildChild { + // If the path at the end of the loop is not equal to '/' and the current node has no child nodes + // the current node needs to roll back to last vaild skippedNode + if path != "/" { + for l := len(*skippedNodes); l > 0; { + skippedNode := (*skippedNodes)[l-1] + *skippedNodes = (*skippedNodes)[:l-1] + if strings.HasSuffix(skippedNode.path, path) { + path = skippedNode.path + n = skippedNode.node + if value.params != nil { + *value.params = (*value.params)[:skippedNode.paramsCount] + } + globalParamsCount = skippedNode.paramsCount + continue walk } - globalParamsCount = skippedNode.paramsCount - continue walk } } - } - // If there is no wildcard pattern, recommend a redirection - if !n.wildChild { // Nothing found. // We can recommend to redirect to the same URL without a // trailing slash if a leaf exists for that path. @@ -614,8 +613,14 @@ walk: // Outer loop for walking the tree return } - // roll back to last vaild skippedNode - if path != "/" { + // Nothing found. We can recommend to redirect to the same URL with an + // extra trailing slash if a leaf exists for that path + value.tsr = path == "/" || + (len(prefix) == len(path)+1 && prefix[len(path)] == '/' && + path == prefix[:len(prefix)-1] && n.handlers != nil) + + // roll back to last valid skippedNode + if !value.tsr && path != "/" { for l := len(*skippedNodes); l > 0; { skippedNode := (*skippedNodes)[l-1] *skippedNodes = (*skippedNodes)[:l-1] @@ -631,11 +636,6 @@ walk: // Outer loop for walking the tree } } - // Nothing found. We can recommend to redirect to the same URL with an - // extra trailing slash if a leaf exists for that path - value.tsr = path == "/" || - (len(prefix) == len(path)+1 && prefix[len(path)] == '/' && - path == prefix[:len(prefix)-1] && n.handlers != nil) return } } diff --git a/tree_test.go b/tree_test.go index 49b3b57ef6..c372339673 100644 --- a/tree_test.go +++ b/tree_test.go @@ -587,7 +587,14 @@ func TestTreeTrailingSlashRedirect(t *testing.T) { "/doc/go1.html", "/no/a", "/no/b", - "/api/hello/:name", + "/api/:page/:name", + "/api/hello/:name/bar/", + "/api/bar/:name", + "/api/baz/foo", + "/api/baz/foo/bar", + "/blog/:p", + "/posts/:b/:c", + "/posts/b/:c/d/", } for _, route := range routes { recv := catchPanic(func() { @@ -613,7 +620,19 @@ func TestTreeTrailingSlashRedirect(t *testing.T) { "/admin/config/", "/admin/config/permissions/", "/doc/", + "/admin/static/", + "/admin/cfg/", + "/admin/cfg/users/", + "/api/hello/x/bar", + "/api/baz/foo/", + "/api/baz/bax/", + "/api/bar/huh/", + "/api/baz/foo/bar/", + "/api/world/abc/", + "/blog/pp/", + "/posts/b/c/d", } + for _, route := range tsrRoutes { value := tree.getValue(route, nil, getSkippedNodes(), false) if value.handlers != nil { @@ -629,7 +648,11 @@ func TestTreeTrailingSlashRedirect(t *testing.T) { "/no/", "/_", "/_/", - "/api/world/abc", + "/api", + "/api/", + "/api/hello/x/foo", + "/api/baz/foo/bad", + "/foo/p/p", } for _, route := range noTsrRoutes { value := tree.getValue(route, nil, getSkippedNodes(), false) From efa3175007d2d4e392bc44e11a6419431686412e Mon Sep 17 00:00:00 2001 From: Quentin ROYER Date: Mon, 1 Nov 2021 01:33:38 +0100 Subject: [PATCH 058/346] Update version.go (#2923) --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 535bfc8273..b9110adbc4 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.3" +const Version = "v1.7.4" 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 059/346] 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 15c2530aa1..c25a909178 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 060/346] Fix grammar (#2933) --- context.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/context.go b/context.go index 58f38c88cb..2aa91e11e0 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 061/346] 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 87fc4c665b..bd8764b194 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 839cf710b5..8d628369ea 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 e9debe59bf..ff1301020e 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 062/346] 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 308af74c36..4c806a5a7d 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 c25eecf9df..8a5d7205aa 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 b9110adbc4..4b69b9b91c 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 063/346] fix: typo (#2958) --- gin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 1d9c9fee44..21513c6826 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 064/346] fix: description error (#2961) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 2aa91e11e0..a97dd4096a 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 065/346] Update README.md (#2954) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cad746d62c..e3d6ab2f84 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 066/346] 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 2f0de1a96e..521d3ef382 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 067/346] 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 a97dd4096a..30a869823c 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 21513c6826..d507b51d28 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 068/346] 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 d507b51d28..94de631ccb 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 22138a8d30..61b84546d8 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 39f1355138..40eba3b202 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 069/346] 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 30a869823c..4fb3ae6dc4 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 - } - 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 + return ip } // ContentType returns the Content-Type header of the request. diff --git a/context_test.go b/context_test.go index c286c0f4cb..4d002c2375 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 94de631ccb..45f4f2cf0f 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 070/346] 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 45f4f2cf0f..dac274c2ba 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 3080fd34fb..ed054bfd25 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 071/346] 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 dac274c2ba..54e1a00c3a 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 072/346] 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 8a5d7205aa..05b9760f63 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 51497a0bac..adacf5ee6d 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 073/346] Update README.md (#2985) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e3d6ab2f84..f78a64ebcb 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 074/346] 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 521d3ef382..a30d349606 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 c372339673..94c533864e 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 075/346] 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 c634e6be05..533204edf2 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 54e1a00c3a..9bd4c46af6 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 4d199df306..1fb994b42c 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 076/346] 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 4fb3ae6dc4..231bb14277 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 077/346] 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 deb71661b8..0414a3453b 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 2342447070..f0b667b215 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 078/346] Update README.md (#2997) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f78a64ebcb..6ae567b314 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 079/346] 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 6ae567b314..b75df63fce 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) From 01363191befaea052a8507ace95efdb70395960a Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Sun, 2 Jan 2022 14:04:07 +0800 Subject: [PATCH 080/346] fix: typo (#3006) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 231bb14277..cccb013a82 100644 --- a/context.go +++ b/context.go @@ -843,7 +843,7 @@ func (c *Context) GetHeader(key string) string { return c.requestHeader(key) } -// GetRawData return stream data. +// GetRawData returns stream data. func (c *Context) GetRawData() ([]byte, error) { return ioutil.ReadAll(c.Request.Body) } From 94153d1e19e20f56f520b31007e667ab6a5caeab Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 2 Jan 2022 19:07:44 +0800 Subject: [PATCH 081/346] test: expose performRequest func (#3012) --- context_test.go | 4 +- gin_test.go | 6 +-- githubapi_test.go | 10 ++-- logger_test.go | 52 ++++++++++----------- middleware_test.go | 17 ++++--- recovery_test.go | 23 +++++---- routergroup_test.go | 4 +- routes_test.go | 111 ++++++++++++++++++++++---------------------- utils_test.go | 8 ++-- 9 files changed, 115 insertions(+), 120 deletions(-) diff --git a/context_test.go b/context_test.go index 4d002c2375..9e02aede13 100644 --- a/context_test.go +++ b/context_test.go @@ -2036,8 +2036,8 @@ func TestRaceParamsContextCopy(t *testing.T) { }(c.Copy(), c.Param("name")) }) } - performRequest(router, "GET", "/name1/api") - performRequest(router, "GET", "/name2/api") + PerformRequest(router, "GET", "/name1/api") + PerformRequest(router, "GET", "/name2/api") wg.Wait() } diff --git a/gin_test.go b/gin_test.go index 21c43d1520..0aece61d16 100644 --- a/gin_test.go +++ b/gin_test.go @@ -395,7 +395,6 @@ func TestNoMethodWithoutGlobalHandlers(t *testing.T) { } func TestRebuild404Handlers(t *testing.T) { - } func TestNoMethodWithGlobalHandlers(t *testing.T) { @@ -491,7 +490,7 @@ func TestEngineHandleContext(t *testing.T) { } assert.NotPanics(t, func() { - w := performRequest(r, "GET", "/") + w := PerformRequest(r, "GET", "/") assert.Equal(t, 301, w.Code) }) } @@ -524,7 +523,7 @@ func TestEngineHandleContextManyReEntries(t *testing.T) { }) assert.NotPanics(t, func() { - w := performRequest(r, "GET", "/"+strconv.Itoa(expectValue-1)) // include 0 value + w := PerformRequest(r, "GET", "/"+strconv.Itoa(expectValue-1)) // include 0 value assert.Equal(t, 200, w.Code) assert.Equal(t, expectValue, w.Body.Len()) }) @@ -636,7 +635,6 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { assert.Nil(t, r.trustedCIDRs) assert.Nil(t, err) } - } func parseCIDR(cidr string) *net.IPNet { diff --git a/githubapi_test.go b/githubapi_test.go index 3f440bceb8..e74bddd55c 100644 --- a/githubapi_test.go +++ b/githubapi_test.go @@ -302,7 +302,7 @@ func TestShouldBindUri(t *testing.T) { }) path, _ := exampleFromPath("/rest/:name/:id") - w := performRequest(router, http.MethodGet, path) + w := PerformRequest(router, http.MethodGet, path) assert.Equal(t, "ShouldBindUri test OK", w.Body.String()) assert.Equal(t, http.StatusOK, w.Code) } @@ -324,7 +324,7 @@ func TestBindUri(t *testing.T) { }) path, _ := exampleFromPath("/rest/:name/:id") - w := performRequest(router, http.MethodGet, path) + w := PerformRequest(router, http.MethodGet, path) assert.Equal(t, "BindUri test OK", w.Body.String()) assert.Equal(t, http.StatusOK, w.Code) } @@ -342,7 +342,7 @@ func TestBindUriError(t *testing.T) { }) path1, _ := exampleFromPath("/new/rest/:num") - w1 := performRequest(router, http.MethodGet, path1) + w1 := PerformRequest(router, http.MethodGet, path1) assert.Equal(t, http.StatusBadRequest, w1.Code) } @@ -358,7 +358,7 @@ func TestRaceContextCopy(t *testing.T) { go readWriteKeys(c.Copy()) c.String(http.StatusOK, "run OK, no panics") }) - w := performRequest(router, http.MethodGet, "/test/copy/race") + w := PerformRequest(router, http.MethodGet, "/test/copy/race") assert.Equal(t, "run OK, no panics", w.Body.String()) } @@ -389,7 +389,7 @@ func TestGithubAPI(t *testing.T) { for _, route := range githubAPI { path, values := exampleFromPath(route.path) - w := performRequest(router, route.method, path) + w := PerformRequest(router, route.method, path) // TEST assert.Contains(t, w.Body.String(), "\"status\":\"good\"") diff --git a/logger_test.go b/logger_test.go index 80961ce173..ec5e6cdccd 100644 --- a/logger_test.go +++ b/logger_test.go @@ -31,7 +31,7 @@ func TestLogger(t *testing.T) { router.HEAD("/example", func(c *Context) {}) router.OPTIONS("/example", func(c *Context) {}) - performRequest(router, "GET", "/example?a=100") + PerformRequest(router, "GET", "/example?a=100") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), "/example") @@ -41,43 +41,43 @@ func TestLogger(t *testing.T) { // like integration tests because they test the whole logging process rather // than individual functions. Im not sure where these should go. buffer.Reset() - performRequest(router, "POST", "/example") + PerformRequest(router, "POST", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "POST") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "PUT", "/example") + PerformRequest(router, "PUT", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "PUT") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "DELETE", "/example") + PerformRequest(router, "DELETE", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "DELETE") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "PATCH", "/example") + PerformRequest(router, "PATCH", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "PATCH") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "HEAD", "/example") + PerformRequest(router, "HEAD", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "HEAD") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "OPTIONS", "/example") + PerformRequest(router, "OPTIONS", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "OPTIONS") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "GET", "/notfound") + PerformRequest(router, "GET", "/notfound") assert.Contains(t, buffer.String(), "404") assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), "/notfound") @@ -95,7 +95,7 @@ func TestLoggerWithConfig(t *testing.T) { router.HEAD("/example", func(c *Context) {}) router.OPTIONS("/example", func(c *Context) {}) - performRequest(router, "GET", "/example?a=100") + PerformRequest(router, "GET", "/example?a=100") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), "/example") @@ -105,43 +105,43 @@ func TestLoggerWithConfig(t *testing.T) { // like integration tests because they test the whole logging process rather // than individual functions. Im not sure where these should go. buffer.Reset() - performRequest(router, "POST", "/example") + PerformRequest(router, "POST", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "POST") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "PUT", "/example") + PerformRequest(router, "PUT", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "PUT") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "DELETE", "/example") + PerformRequest(router, "DELETE", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "DELETE") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "PATCH", "/example") + PerformRequest(router, "PATCH", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "PATCH") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "HEAD", "/example") + PerformRequest(router, "HEAD", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "HEAD") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "OPTIONS", "/example") + PerformRequest(router, "OPTIONS", "/example") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "OPTIONS") assert.Contains(t, buffer.String(), "/example") buffer.Reset() - performRequest(router, "GET", "/notfound") + PerformRequest(router, "GET", "/notfound") assert.Contains(t, buffer.String(), "404") assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), "/notfound") @@ -169,7 +169,7 @@ func TestLoggerWithFormatter(t *testing.T) { ) })) router.GET("/example", func(c *Context) {}) - performRequest(router, "GET", "/example?a=100") + PerformRequest(router, "GET", "/example?a=100") // output test assert.Contains(t, buffer.String(), "[FORMATTER TEST]") @@ -209,7 +209,7 @@ func TestLoggerWithConfigFormatting(t *testing.T) { c.Request.Header.Set("X-Forwarded-For", "20.20.20.20") gotKeys = c.Keys }) - performRequest(router, "GET", "/example?a=100") + PerformRequest(router, "GET", "/example?a=100") // output test assert.Contains(t, buffer.String(), "[FORMATTER TEST]") @@ -228,7 +228,6 @@ func TestLoggerWithConfigFormatting(t *testing.T) { assert.Equal(t, "/example?a=100", gotParam.Path) assert.Empty(t, gotParam.ErrorMessage) assert.Equal(t, gotKeys, gotParam.Keys) - } func TestDefaultLogFormatter(t *testing.T) { @@ -282,7 +281,6 @@ func TestDefaultLogFormatter(t *testing.T) { assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 |\x1b[97;42m 200 \x1b[0m| 5s | 20.20.20.20 |\x1b[97;44m GET \x1b[0m \"/\"\n", defaultLogFormatter(termTrueParam)) assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 |\x1b[97;42m 200 \x1b[0m| 2743h29m3s | 20.20.20.20 |\x1b[97;44m GET \x1b[0m \"/\"\n", defaultLogFormatter(termTrueLongDurationParam)) - } func TestColorForMethod(t *testing.T) { @@ -369,15 +367,15 @@ func TestErrorLogger(t *testing.T) { c.String(http.StatusInternalServerError, "hola!") }) - w := performRequest(router, "GET", "/error") + w := PerformRequest(router, "GET", "/error") assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "{\"error\":\"this is an error\"}", w.Body.String()) - w = performRequest(router, "GET", "/abort") + w = PerformRequest(router, "GET", "/abort") assert.Equal(t, http.StatusUnauthorized, w.Code) assert.Equal(t, "{\"error\":\"no authorized\"}", w.Body.String()) - w = performRequest(router, "GET", "/print") + w = PerformRequest(router, "GET", "/print") assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, "hola!{\"error\":\"this is an error\"}", w.Body.String()) } @@ -389,11 +387,11 @@ func TestLoggerWithWriterSkippingPaths(t *testing.T) { router.GET("/logged", func(c *Context) {}) router.GET("/skipped", func(c *Context) {}) - performRequest(router, "GET", "/logged") + PerformRequest(router, "GET", "/logged") assert.Contains(t, buffer.String(), "200") buffer.Reset() - performRequest(router, "GET", "/skipped") + PerformRequest(router, "GET", "/skipped") assert.Contains(t, buffer.String(), "") } @@ -407,11 +405,11 @@ func TestLoggerWithConfigSkippingPaths(t *testing.T) { router.GET("/logged", func(c *Context) {}) router.GET("/skipped", func(c *Context) {}) - performRequest(router, "GET", "/logged") + PerformRequest(router, "GET", "/logged") assert.Contains(t, buffer.String(), "200") buffer.Reset() - performRequest(router, "GET", "/skipped") + PerformRequest(router, "GET", "/skipped") assert.Contains(t, buffer.String(), "") } diff --git a/middleware_test.go b/middleware_test.go index 4b4afd4af0..e0a756c3de 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -35,7 +35,7 @@ func TestMiddlewareGeneralCase(t *testing.T) { signature += " XX " }) // RUN - w := performRequest(router, "GET", "/") + w := PerformRequest(router, "GET", "/") // TEST assert.Equal(t, http.StatusOK, w.Code) @@ -71,7 +71,7 @@ func TestMiddlewareNoRoute(t *testing.T) { signature += " X " }) // RUN - w := performRequest(router, "GET", "/") + w := PerformRequest(router, "GET", "/") // TEST assert.Equal(t, http.StatusNotFound, w.Code) @@ -108,7 +108,7 @@ func TestMiddlewareNoMethodEnabled(t *testing.T) { signature += " XX " }) // RUN - w := performRequest(router, "GET", "/") + w := PerformRequest(router, "GET", "/") // TEST assert.Equal(t, http.StatusMethodNotAllowed, w.Code) @@ -149,7 +149,7 @@ func TestMiddlewareNoMethodDisabled(t *testing.T) { }) // RUN - w := performRequest(router, "GET", "/") + w := PerformRequest(router, "GET", "/") // TEST assert.Equal(t, http.StatusNotFound, w.Code) @@ -175,7 +175,7 @@ func TestMiddlewareAbort(t *testing.T) { }) // RUN - w := performRequest(router, "GET", "/") + w := PerformRequest(router, "GET", "/") // TEST assert.Equal(t, http.StatusUnauthorized, w.Code) @@ -190,14 +190,13 @@ func TestMiddlewareAbortHandlersChainAndNext(t *testing.T) { c.Next() c.AbortWithStatus(http.StatusGone) signature += "B" - }) router.GET("/", func(c *Context) { signature += "C" c.Next() }) // RUN - w := performRequest(router, "GET", "/") + w := PerformRequest(router, "GET", "/") // TEST assert.Equal(t, http.StatusGone, w.Code) @@ -220,7 +219,7 @@ func TestMiddlewareFailHandlersChain(t *testing.T) { signature += "C" }) // RUN - w := performRequest(router, "GET", "/") + w := PerformRequest(router, "GET", "/") // TEST assert.Equal(t, http.StatusInternalServerError, w.Code) @@ -247,7 +246,7 @@ func TestMiddlewareWrite(t *testing.T) { }) }) - w := performRequest(router, "GET", "/") + w := PerformRequest(router, "GET", "/") assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, strings.Replace("hola\nbar{\"foo\":\"bar\"}{\"foo\":\"bar\"}event:test\ndata:message\n\n", " ", "", -1), strings.Replace(w.Body.String(), " ", "", -1)) diff --git a/recovery_test.go b/recovery_test.go index d164bfa369..ac3669a419 100644 --- a/recovery_test.go +++ b/recovery_test.go @@ -27,7 +27,7 @@ func TestPanicClean(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := performRequest(router, "GET", "/recovery", + w := PerformRequest(router, "GET", "/recovery", header{ Key: "Host", Value: "www.google.com", @@ -57,7 +57,7 @@ func TestPanicInHandler(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := performRequest(router, "GET", "/recovery") + w := PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Contains(t, buffer.String(), "panic recovered") @@ -68,7 +68,7 @@ func TestPanicInHandler(t *testing.T) { // Debug mode prints the request SetMode(DebugMode) // RUN - w = performRequest(router, "GET", "/recovery") + w = PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Contains(t, buffer.String(), "GET /recovery") @@ -85,7 +85,7 @@ func TestPanicWithAbort(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := performRequest(router, "GET", "/recovery") + w := PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) } @@ -122,7 +122,6 @@ func TestPanicWithBrokenPipe(t *testing.T) { for errno, expectMsg := range expectMsgs { t.Run(expectMsg, func(t *testing.T) { - var buf bytes.Buffer router := New() @@ -137,7 +136,7 @@ func TestPanicWithBrokenPipe(t *testing.T) { panic(e) }) // RUN - w := performRequest(router, "GET", "/recovery") + w := PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, expectCode, w.Code) assert.Contains(t, strings.ToLower(buf.String()), expectMsg) @@ -158,7 +157,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := performRequest(router, "GET", "/recovery") + w := PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "panic recovered") @@ -169,7 +168,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) { // Debug mode prints the request SetMode(DebugMode) // RUN - w = performRequest(router, "GET", "/recovery") + w = PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "GET /recovery") @@ -193,7 +192,7 @@ func TestCustomRecovery(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := performRequest(router, "GET", "/recovery") + w := PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "panic recovered") @@ -204,7 +203,7 @@ func TestCustomRecovery(t *testing.T) { // Debug mode prints the request SetMode(DebugMode) // RUN - w = performRequest(router, "GET", "/recovery") + w = PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "GET /recovery") @@ -228,7 +227,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := performRequest(router, "GET", "/recovery") + w := PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "panic recovered") @@ -239,7 +238,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) { // Debug mode prints the request SetMode(DebugMode) // RUN - w = performRequest(router, "GET", "/recovery") + w = PerformRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "GET /recovery") diff --git a/routergroup_test.go b/routergroup_test.go index d6d8b45269..232f595b0a 100644 --- a/routergroup_test.go +++ b/routergroup_test.go @@ -80,11 +80,11 @@ func performRequestInGroup(t *testing.T, method string) { panic("unknown method") } - w := performRequest(router, method, "/v1/login/test") + w := PerformRequest(router, method, "/v1/login/test") assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, "the method was "+method+" and index 3", w.Body.String()) - w = performRequest(router, method, "/v1/test") + w = PerformRequest(router, method, "/v1/test") assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, "the method was "+method+" and index 1", w.Body.String()) } diff --git a/routes_test.go b/routes_test.go index ffe3469e7c..b3f0c47b88 100644 --- a/routes_test.go +++ b/routes_test.go @@ -21,7 +21,8 @@ type header struct { Value string } -func performRequest(r http.Handler, method, path string, headers ...header) *httptest.ResponseRecorder { +// PerformRequest for testing gin router. +func PerformRequest(r http.Handler, method, path string, headers ...header) *httptest.ResponseRecorder { req := httptest.NewRequest(method, path, nil) for _, h := range headers { req.Header.Add(h.Key, h.Value) @@ -42,11 +43,11 @@ func testRouteOK(method string, t *testing.T) { passed = true }) - w := performRequest(r, method, "/test") + w := PerformRequest(r, method, "/test") assert.True(t, passed) assert.Equal(t, http.StatusOK, w.Code) - performRequest(r, method, "/test2") + PerformRequest(r, method, "/test2") assert.True(t, passedAny) } @@ -58,7 +59,7 @@ func testRouteNotOK(method string, t *testing.T) { passed = true }) - w := performRequest(router, method, "/test") + w := PerformRequest(router, method, "/test") assert.False(t, passed) assert.Equal(t, http.StatusNotFound, w.Code) @@ -79,7 +80,7 @@ func testRouteNotOK2(method string, t *testing.T) { passed = true }) - w := performRequest(router, method, "/test") + w := PerformRequest(router, method, "/test") assert.False(t, passed) assert.Equal(t, http.StatusMethodNotAllowed, w.Code) @@ -99,7 +100,7 @@ func TestRouterMethod(t *testing.T) { c.String(http.StatusOK, "sup3") }) - w := performRequest(router, http.MethodPut, "/hey") + w := PerformRequest(router, http.MethodPut, "/hey") assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "called", w.Body.String()) @@ -150,50 +151,50 @@ func TestRouteRedirectTrailingSlash(t *testing.T) { router.POST("/path3", func(c *Context) {}) router.PUT("/path4/", func(c *Context) {}) - w := performRequest(router, http.MethodGet, "/path/") + w := PerformRequest(router, http.MethodGet, "/path/") assert.Equal(t, "/path", w.Header().Get("Location")) assert.Equal(t, http.StatusMovedPermanently, w.Code) - w = performRequest(router, http.MethodGet, "/path2") + w = PerformRequest(router, http.MethodGet, "/path2") assert.Equal(t, "/path2/", w.Header().Get("Location")) assert.Equal(t, http.StatusMovedPermanently, w.Code) - w = performRequest(router, http.MethodPost, "/path3/") + w = PerformRequest(router, http.MethodPost, "/path3/") assert.Equal(t, "/path3", w.Header().Get("Location")) assert.Equal(t, http.StatusTemporaryRedirect, w.Code) - w = performRequest(router, http.MethodPut, "/path4") + w = PerformRequest(router, http.MethodPut, "/path4") assert.Equal(t, "/path4/", w.Header().Get("Location")) assert.Equal(t, http.StatusTemporaryRedirect, w.Code) - w = performRequest(router, http.MethodGet, "/path") + w = PerformRequest(router, http.MethodGet, "/path") assert.Equal(t, http.StatusOK, w.Code) - w = performRequest(router, http.MethodGet, "/path2/") + w = PerformRequest(router, http.MethodGet, "/path2/") assert.Equal(t, http.StatusOK, w.Code) - w = performRequest(router, http.MethodPost, "/path3") + w = PerformRequest(router, http.MethodPost, "/path3") assert.Equal(t, http.StatusOK, w.Code) - w = performRequest(router, http.MethodPut, "/path4/") + w = PerformRequest(router, http.MethodPut, "/path4/") assert.Equal(t, http.StatusOK, w.Code) - w = performRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/api"}) + w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/api"}) assert.Equal(t, "/api/path2/", w.Header().Get("Location")) assert.Equal(t, 301, w.Code) - w = performRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"}) + w = PerformRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"}) assert.Equal(t, 200, w.Code) router.RedirectTrailingSlash = false - w = performRequest(router, http.MethodGet, "/path/") + w = PerformRequest(router, http.MethodGet, "/path/") assert.Equal(t, http.StatusNotFound, w.Code) - w = performRequest(router, http.MethodGet, "/path2") + w = PerformRequest(router, http.MethodGet, "/path2") assert.Equal(t, http.StatusNotFound, w.Code) - w = performRequest(router, http.MethodPost, "/path3/") + w = PerformRequest(router, http.MethodPost, "/path3/") assert.Equal(t, http.StatusNotFound, w.Code) - w = performRequest(router, http.MethodPut, "/path4") + w = PerformRequest(router, http.MethodPut, "/path4") assert.Equal(t, http.StatusNotFound, w.Code) } @@ -207,19 +208,19 @@ func TestRouteRedirectFixedPath(t *testing.T) { router.POST("/PATH3", func(c *Context) {}) router.POST("/Path4/", func(c *Context) {}) - w := performRequest(router, http.MethodGet, "/PATH") + w := PerformRequest(router, http.MethodGet, "/PATH") assert.Equal(t, "/path", w.Header().Get("Location")) assert.Equal(t, http.StatusMovedPermanently, w.Code) - w = performRequest(router, http.MethodGet, "/path2") + w = PerformRequest(router, http.MethodGet, "/path2") assert.Equal(t, "/Path2", w.Header().Get("Location")) assert.Equal(t, http.StatusMovedPermanently, w.Code) - w = performRequest(router, http.MethodPost, "/path3") + w = PerformRequest(router, http.MethodPost, "/path3") assert.Equal(t, "/PATH3", w.Header().Get("Location")) assert.Equal(t, http.StatusTemporaryRedirect, w.Code) - w = performRequest(router, http.MethodPost, "/path4") + w = PerformRequest(router, http.MethodPost, "/path4") assert.Equal(t, "/Path4/", w.Header().Get("Location")) assert.Equal(t, http.StatusTemporaryRedirect, w.Code) } @@ -248,7 +249,7 @@ func TestRouteParamsByName(t *testing.T) { assert.False(t, ok) }) - w := performRequest(router, http.MethodGet, "/test/john/smith/is/super/great") + w := PerformRequest(router, http.MethodGet, "/test/john/smith/is/super/great") assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "john", name) @@ -281,7 +282,7 @@ func TestRouteParamsByNameWithExtraSlash(t *testing.T) { assert.False(t, ok) }) - w := performRequest(router, http.MethodGet, "//test//john//smith//is//super//great") + w := PerformRequest(router, http.MethodGet, "//test//john//smith//is//super//great") assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "john", name) @@ -309,16 +310,16 @@ func TestRouteStaticFile(t *testing.T) { router.Static("/using_static", dir) router.StaticFile("/result", f.Name()) - w := performRequest(router, http.MethodGet, "/using_static/"+filename) - w2 := performRequest(router, http.MethodGet, "/result") + w := PerformRequest(router, http.MethodGet, "/using_static/"+filename) + w2 := PerformRequest(router, http.MethodGet, "/result") assert.Equal(t, w, w2) assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "Gin Web Framework", w.Body.String()) assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) - w3 := performRequest(router, http.MethodHead, "/using_static/"+filename) - w4 := performRequest(router, http.MethodHead, "/result") + w3 := PerformRequest(router, http.MethodHead, "/using_static/"+filename) + w4 := PerformRequest(router, http.MethodHead, "/result") assert.Equal(t, w3, w4) assert.Equal(t, http.StatusOK, w3.Code) @@ -329,7 +330,7 @@ func TestRouteStaticListingDir(t *testing.T) { router := New() router.StaticFS("/", Dir("./", true)) - w := performRequest(router, http.MethodGet, "/") + w := PerformRequest(router, http.MethodGet, "/") assert.Equal(t, http.StatusOK, w.Code) assert.Contains(t, w.Body.String(), "gin.go") @@ -341,7 +342,7 @@ func TestRouteStaticNoListing(t *testing.T) { router := New() router.Static("/", "./") - w := performRequest(router, http.MethodGet, "/") + w := PerformRequest(router, http.MethodGet, "/") assert.Equal(t, http.StatusNotFound, w.Code) assert.NotContains(t, w.Body.String(), "gin.go") @@ -356,7 +357,7 @@ func TestRouterMiddlewareAndStatic(t *testing.T) { }) static.Static("/", "./") - w := performRequest(router, http.MethodGet, "/gin.go") + w := PerformRequest(router, http.MethodGet, "/gin.go") assert.Equal(t, http.StatusOK, w.Code) assert.Contains(t, w.Body.String(), "package gin") @@ -372,13 +373,13 @@ func TestRouteNotAllowedEnabled(t *testing.T) { router := New() router.HandleMethodNotAllowed = true router.POST("/path", func(c *Context) {}) - w := performRequest(router, http.MethodGet, "/path") + w := PerformRequest(router, http.MethodGet, "/path") assert.Equal(t, http.StatusMethodNotAllowed, w.Code) router.NoMethod(func(c *Context) { c.String(http.StatusTeapot, "responseText") }) - w = performRequest(router, http.MethodGet, "/path") + w = PerformRequest(router, http.MethodGet, "/path") assert.Equal(t, "responseText", w.Body.String()) assert.Equal(t, http.StatusTeapot, w.Code) } @@ -389,7 +390,7 @@ func TestRouteNotAllowedEnabled2(t *testing.T) { // add one methodTree to trees router.addRoute(http.MethodPost, "/", HandlersChain{func(_ *Context) {}}) router.GET("/path2", func(c *Context) {}) - w := performRequest(router, http.MethodPost, "/path2") + w := PerformRequest(router, http.MethodPost, "/path2") assert.Equal(t, http.StatusMethodNotAllowed, w.Code) } @@ -397,13 +398,13 @@ func TestRouteNotAllowedDisabled(t *testing.T) { router := New() router.HandleMethodNotAllowed = false router.POST("/path", func(c *Context) {}) - w := performRequest(router, http.MethodGet, "/path") + w := PerformRequest(router, http.MethodGet, "/path") assert.Equal(t, http.StatusNotFound, w.Code) router.NoMethod(func(c *Context) { c.String(http.StatusTeapot, "responseText") }) - w = performRequest(router, http.MethodGet, "/path") + w = PerformRequest(router, http.MethodGet, "/path") assert.Equal(t, "404 page not found", w.Body.String()) assert.Equal(t, http.StatusNotFound, w.Code) } @@ -423,7 +424,7 @@ func TestRouterNotFoundWithRemoveExtraSlash(t *testing.T) { {"/nope", http.StatusNotFound, ""}, // NotFound } for _, tr := range testRoutes { - w := performRequest(router, "GET", tr.route) + w := PerformRequest(router, "GET", tr.route) assert.Equal(t, tr.code, w.Code) if w.Code != http.StatusNotFound { assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) @@ -453,7 +454,7 @@ func TestRouterNotFound(t *testing.T) { {"/nope", http.StatusNotFound, ""}, // NotFound } for _, tr := range testRoutes { - w := performRequest(router, http.MethodGet, tr.route) + w := PerformRequest(router, http.MethodGet, tr.route) assert.Equal(t, tr.code, w.Code) if w.Code != http.StatusNotFound { assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) @@ -466,20 +467,20 @@ func TestRouterNotFound(t *testing.T) { c.AbortWithStatus(http.StatusNotFound) notFound = true }) - w := performRequest(router, http.MethodGet, "/nope") + w := PerformRequest(router, http.MethodGet, "/nope") assert.Equal(t, http.StatusNotFound, w.Code) assert.True(t, notFound) // Test other method than GET (want 307 instead of 301) router.PATCH("/path", func(c *Context) {}) - w = performRequest(router, http.MethodPatch, "/path/") + w = PerformRequest(router, http.MethodPatch, "/path/") assert.Equal(t, http.StatusTemporaryRedirect, w.Code) assert.Equal(t, "map[Location:[/path]]", fmt.Sprint(w.Header())) // Test special case where no node for the prefix "/" exists router = New() router.GET("/a", func(c *Context) {}) - w = performRequest(router, http.MethodGet, "/") + w = PerformRequest(router, http.MethodGet, "/") assert.Equal(t, http.StatusNotFound, w.Code) // Reproduction test for the bug of issue #2843 @@ -492,9 +493,9 @@ func TestRouterNotFound(t *testing.T) { router.GET("/logout", func(c *Context) { c.String(200, "logout") }) - w = performRequest(router, http.MethodGet, "/login") + w = PerformRequest(router, http.MethodGet, "/login") assert.Equal(t, "login", w.Body.String()) - w = performRequest(router, http.MethodGet, "/logout") + w = PerformRequest(router, http.MethodGet, "/logout") assert.Equal(t, "logout", w.Body.String()) } @@ -505,10 +506,10 @@ func TestRouterStaticFSNotFound(t *testing.T) { c.String(404, "non existent") }) - w := performRequest(router, http.MethodGet, "/nonexistent") + w := PerformRequest(router, http.MethodGet, "/nonexistent") assert.Equal(t, "non existent", w.Body.String()) - w = performRequest(router, http.MethodHead, "/nonexistent") + w = PerformRequest(router, http.MethodHead, "/nonexistent") assert.Equal(t, "non existent", w.Body.String()) } @@ -518,7 +519,7 @@ func TestRouterStaticFSFileNotFound(t *testing.T) { router.StaticFS("/", http.FileSystem(http.Dir("."))) assert.NotPanics(t, func() { - performRequest(router, http.MethodGet, "/nonexistent") + PerformRequest(router, http.MethodGet, "/nonexistent") }) } @@ -535,11 +536,11 @@ func TestMiddlewareCalledOnceByRouterStaticFSNotFound(t *testing.T) { router.StaticFS("/", http.FileSystem(http.Dir("/thisreallydoesntexist/"))) // First access - performRequest(router, http.MethodGet, "/nonexistent") + PerformRequest(router, http.MethodGet, "/nonexistent") assert.Equal(t, 1, middlewareCalledNum) // Second access - performRequest(router, http.MethodHead, "/nonexistent") + PerformRequest(router, http.MethodHead, "/nonexistent") assert.Equal(t, 2, middlewareCalledNum) } @@ -558,7 +559,7 @@ func TestRouteRawPath(t *testing.T) { assert.Equal(t, "222", num) }) - w := performRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/222") + w := PerformRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/222") assert.Equal(t, http.StatusOK, w.Code) } @@ -578,7 +579,7 @@ func TestRouteRawPathNoUnescape(t *testing.T) { assert.Equal(t, "333", num) }) - w := performRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/333") + w := PerformRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/333") assert.Equal(t, http.StatusOK, w.Code) } @@ -589,7 +590,7 @@ func TestRouteServeErrorWithWriteHeader(t *testing.T) { c.Next() }) - w := performRequest(route, http.MethodGet, "/NotFound") + w := PerformRequest(route, http.MethodGet, "/NotFound") assert.Equal(t, 421, w.Code) assert.Equal(t, 0, w.Body.Len()) } @@ -623,7 +624,7 @@ func TestRouteContextHoldsFullPath(t *testing.T) { } for _, route := range routes { - w := performRequest(router, http.MethodGet, route) + w := PerformRequest(router, http.MethodGet, route) assert.Equal(t, http.StatusOK, w.Code) } @@ -633,6 +634,6 @@ func TestRouteContextHoldsFullPath(t *testing.T) { assert.Equal(t, "", c.FullPath()) }) - w := performRequest(router, http.MethodGet, "/not-found") + w := PerformRequest(router, http.MethodGet, "/not-found") assert.Equal(t, http.StatusNotFound, w.Code) } diff --git a/utils_test.go b/utils_test.go index cc486c35ed..b50914f25e 100644 --- a/utils_test.go +++ b/utils_test.go @@ -45,11 +45,11 @@ func TestWrap(t *testing.T) { fmt.Fprint(w, "hola!") })) - w := performRequest(router, "POST", "/path") + w := PerformRequest(router, "POST", "/path") assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, "hello", w.Body.String()) - w = performRequest(router, "GET", "/path2") + w = PerformRequest(router, "GET", "/path2") assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, "hola!", w.Body.String()) } @@ -119,13 +119,13 @@ func TestBindMiddleware(t *testing.T) { called = true value = c.MustGet(BindKey).(*bindTestStruct) }) - performRequest(router, "GET", "/?foo=hola&bar=10") + PerformRequest(router, "GET", "/?foo=hola&bar=10") assert.True(t, called) assert.Equal(t, "hola", value.Foo) assert.Equal(t, 10, value.Bar) called = false - performRequest(router, "GET", "/?foo=hola&bar=1") + PerformRequest(router, "GET", "/?foo=hola&bar=1") assert.False(t, called) assert.Panics(t, func() { From 6868ed18cc00f4ee500710462b5551a5e8c116c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Jan 2022 17:25:31 +0800 Subject: [PATCH 082/346] Bump github.com/go-playground/validator/v10 from 10.9.0 to 10.10.0 (#3013) 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 05b9760f63..118eb762c1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.9.0 + github.com/go-playground/validator/v10 v10.10.0 github.com/goccy/go-json v0.8.1 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 diff --git a/go.sum b/go.sum index adacf5ee6d..c16d8c81a9 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= 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/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= 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= From 336ce0d47532e89657b711af52bf5a8e01128755 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 08:15:12 +0800 Subject: [PATCH 083/346] Bump github.com/goccy/go-json from 0.8.1 to 0.9.0 (#3021) Bumps [github.com/goccy/go-json](https://github.com/goccy/go-json) from 0.8.1 to 0.9.0. - [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.8.1...v0.9.0) --- 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 118eb762c1..35488359d0 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.10.0 - github.com/goccy/go-json v0.8.1 + github.com/goccy/go-json v0.9.0 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 c16d8c81a9..59efbf75d0 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.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -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/goccy/go-json v0.9.0 h1:2flW7bkbrRgU8VuDi0WXDqTmPimjv1thfxkPe8sug+8= +github.com/goccy/go-json v0.9.0/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 1b28e2b0303b6e5ecdea70890ba1ee8c5950892b Mon Sep 17 00:00:00 2001 From: jarodsong6 Date: Wed, 12 Jan 2022 23:12:32 +0900 Subject: [PATCH 084/346] Fix typo (#3023) Co-authored-by: lin.song --- ginS/gins.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ginS/gins.go b/ginS/gins.go index ed054bfd25..0802e08567 100644 --- a/ginS/gins.go +++ b/ginS/gins.go @@ -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. i.e. the middlewares attached though Use() will be +// Use attaches a global middleware to the router. i.e. the middlewares 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 Use(middlewares ...gin.HandlerFunc) gin.IRoutes { From 580e7da6eed01e2926de1240ec31f6473cd1a2af Mon Sep 17 00:00:00 2001 From: Waynerv Date: Thu, 20 Jan 2022 22:33:35 +0800 Subject: [PATCH 085/346] Remove incorrect comments about context.Bind() and improve docs (#3028) --- context.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/context.go b/context.go index cccb013a82..d69df70b24 100644 --- a/context.go +++ b/context.go @@ -601,11 +601,10 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error return err } -// Bind checks the Content-Type to select a binding engine automatically, -// Depending on the "Content-Type" header different bindings are used: +// Bind checks the Method and Content-Type to select a binding engine automatically, +// Depending on the "Content-Type" header different bindings are used, for example: // "application/json" --> JSON binding // "application/xml" --> XML binding -// otherwise --> returns an error. // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It decodes the json payload into the struct specified as a pointer. // It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid. @@ -660,14 +659,13 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error { return nil } -// ShouldBind checks the Content-Type to select a binding engine automatically, -// Depending on the "Content-Type" header different bindings are used: +// ShouldBind checks the Method and Content-Type to select a binding engine automatically, +// Depending on the "Content-Type" header different bindings are used, for example: // "application/json" --> JSON binding // "application/xml" --> XML binding -// otherwise --> returns an error // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It decodes the json payload into the struct specified as a pointer. -// Like c.Bind() but this method does not set the response status code to 400 and abort if the json is not valid. +// Like c.Bind() but this method does not set the response status code to 400 or abort if input is not valid. func (c *Context) ShouldBind(obj interface{}) error { b := binding.Default(c.Request.Method, c.ContentType()) return c.ShouldBindWith(obj, b) From 41d38fb68cbd4460994d9a6b30229ce1009db01d Mon Sep 17 00:00:00 2001 From: MichaelDeSteven <51652084+MichaelDeSteven@users.noreply.github.com> Date: Sat, 5 Feb 2022 09:30:38 +0800 Subject: [PATCH 086/346] fix typo (#3044) --- tree.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tree.go b/tree.go index a30d349606..18e50ee363 100644 --- a/tree.go +++ b/tree.go @@ -81,7 +81,7 @@ func longestCommonPrefix(a, b string) int { return i } -// addChild will add a child node, keeping wildcards at the end +// addChild will add a child node, keeping wildcardChild at the end func (n *node) addChild(child *node) { if n.wildChild && len(n.children) > 0 { wildcardChild := n.children[len(n.children)-1] @@ -296,7 +296,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) break } - // The wildcard name must not contain ':' and '*' + // The wildcard name must only contain one ':' or '*' character if !valid { panic("only one wildcard per path segment is allowed, has: '" + wildcard + "' in path '" + fullPath + "'") From b94075ff1dab162c8df7f0d1ee3f6a854eb5c4d1 Mon Sep 17 00:00:00 2001 From: Sasha Melentyev Date: Sat, 5 Feb 2022 04:36:38 +0300 Subject: [PATCH 087/346] ci: bump golangci-lint version (#3037) --- .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 c25a909178..f3927b3d46 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.43.0 + version: v1.44.0 args: --verbose test: needs: lint From c19374c4711a5587e02b40d25656e3551d3bded8 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 5 Feb 2022 20:57:13 +0800 Subject: [PATCH 088/346] feat(CodeQL): Discover vulnerabilities across a codebase with CodeQL (#3049) * feat(CodeQL): Discover vulnerabilities across a codebase with CodeQL Signed-off-by: Bo-Yi Wu * fix: unknown directive: retract Signed-off-by: Bo-Yi Wu --- .github/workflows/codeql.yml | 49 ++++++++++++++++++++++++++++++++++++ go.mod | 2 -- 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..4a081e0cd1 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,49 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '0 17 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + permissions: + # required for all workflows + security-events: write + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + # TODO: Enable for javascript later + language: [ 'go'] + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/go.mod b/go.mod index 35488359d0..b088f26bb9 100644 --- a/go.mod +++ b/go.mod @@ -13,5 +13,3 @@ require ( google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 ) - -retract v1.7.5 From b40ded18375ad1fd955bf32ee74227c1e5294c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=9B=E5=8F=94?= Date: Sat, 5 Feb 2022 21:13:20 +0800 Subject: [PATCH 089/346] Add h2c support (#1398) --- gin.go | 22 ++++++++++++++++++---- gin_test.go | 34 ++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 1 + 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/gin.go b/gin.go index 9bd4c46af6..b54dbd8a62 100644 --- a/gin.go +++ b/gin.go @@ -16,6 +16,8 @@ import ( "github.com/gin-gonic/gin/internal/bytesconv" "github.com/gin-gonic/gin/render" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" ) const defaultMultipartMemory = 32 << 20 // 32 MB @@ -141,6 +143,9 @@ type Engine struct { // method call. MaxMultipartMemory int64 + // Enable h2c support. + UseH2C bool + delims render.Delims secureJSONPrefix string HTMLRender render.HTMLRender @@ -207,6 +212,15 @@ func Default() *Engine { return engine } +func (engine *Engine) Handler() http.Handler { + if !engine.UseH2C { + return engine + } + + h2s := &http2.Server{} + return h2c.NewHandler(engine, h2s) +} + func (engine *Engine) allocateContext() *Context { v := make(Params, 0, engine.maxParams) skippedNodes := make([]skippedNode, 0, engine.maxSections) @@ -361,7 +375,7 @@ func (engine *Engine) Run(addr ...string) (err error) { address := resolveAddress(addr) debugPrint("Listening and serving HTTP on %s\n", address) - err = http.ListenAndServe(address, engine) + err = http.ListenAndServe(address, engine.Handler()) return } @@ -480,7 +494,7 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) { "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.Handler()) return } @@ -503,7 +517,7 @@ func (engine *Engine) RunUnix(file string) (err error) { defer listener.Close() defer os.Remove(file) - err = http.Serve(listener, engine) + err = http.Serve(listener, engine.Handler()) return } @@ -540,7 +554,7 @@ func (engine *Engine) RunListener(listener net.Listener) (err error) { "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.Handler()) return } diff --git a/gin_test.go b/gin_test.go index 0aece61d16..629a109b2d 100644 --- a/gin_test.go +++ b/gin_test.go @@ -19,6 +19,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "golang.org/x/net/http2" ) func formatAsDate(t time.Time) string { @@ -79,6 +80,39 @@ func TestLoadHTMLGlobDebugMode(t *testing.T) { assert.Equal(t, "

Hello world

", string(resp)) } +func TestH2c(t *testing.T) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + fmt.Println(err) + } + r := Default() + r.UseH2C = true + r.GET("/", func(c *Context) { + c.String(200, "

Hello world

") + }) + go http.Serve(ln, r.Handler()) + defer ln.Close() + + url := "http://" + ln.Addr().String() + "/" + + http := http.Client{ + Transport: &http2.Transport{ + AllowHTTP: true, + DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) { + return net.Dial(netw, addr) + }, + }, + } + + res, err := http.Get(url) + if err != nil { + fmt.Println(err) + } + + resp, _ := ioutil.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp)) +} + func TestLoadHTMLGlobTestMode(t *testing.T) { ts := setupHTMLFiles( t, diff --git a/go.mod b/go.mod index b088f26bb9..b59e590728 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/mattn/go-isatty v0.0.14 github.com/stretchr/testify v1.7.0 github.com/ugorji/go/codec v1.2.6 + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 59efbf75d0..c01ba52431 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,7 @@ github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= 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/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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= From 87e40d6b150f62f26ccf212ae785d24baeb4f6cd Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 7 Feb 2022 23:15:44 +0800 Subject: [PATCH 090/346] feat: fix lint error (#3050) --- gin_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gin_test.go b/gin_test.go index 629a109b2d..ae1762efd7 100644 --- a/gin_test.go +++ b/gin_test.go @@ -90,7 +90,12 @@ func TestH2c(t *testing.T) { r.GET("/", func(c *Context) { c.String(200, "

Hello world

") }) - go http.Serve(ln, r.Handler()) + go func() { + err := http.Serve(ln, r.Handler()) + if err != nil { + fmt.Println(err) + } + }() defer ln.Close() url := "http://" + ln.Addr().String() + "/" From 375714258462e46df07337b31dae4370d03ab28d Mon Sep 17 00:00:00 2001 From: bestgopher <84328409@qq.com> Date: Mon, 14 Feb 2022 14:39:57 +0800 Subject: [PATCH 091/346] Update routergroup.go (#3056) --- routergroup.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routergroup.go b/routergroup.go index 27d7aad671..b84fcca3d8 100644 --- a/routergroup.go +++ b/routergroup.go @@ -12,7 +12,7 @@ import ( ) var ( - // reg match english letters for http method name + // regEnLetter matches english letters for http method name regEnLetter = regexp.MustCompile("^[A-Z]+$") // anyMethods for RouterGroup Any method From 5f0b6cdfc4313b0d43e15e03ab9c6d6e2e66af03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Mar 2022 20:42:58 +0800 Subject: [PATCH 092/346] Bump github.com/goccy/go-json from 0.9.0 to 0.9.5 (#3069) 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 b59e590728..8087227e67 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.10.0 - github.com/goccy/go-json v0.9.0 + github.com/goccy/go-json v0.9.5 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 c01ba52431..7f892453b7 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.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/goccy/go-json v0.9.0 h1:2flW7bkbrRgU8VuDi0WXDqTmPimjv1thfxkPe8sug+8= -github.com/goccy/go-json v0.9.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.5 h1:ooSMW526ZjK+EaL5elrSyN2EzIfi/3V0m4+HJEDYLik= +github.com/goccy/go-json v0.9.5/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 ecadd825920ab42d188ba4cd6bf36360b656ef55 Mon Sep 17 00:00:00 2001 From: phithon Date: Tue, 15 Mar 2022 11:24:17 +0800 Subject: [PATCH 093/346] doc: change the form name of upload example code (#3076) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b75df63fce..d3fa075764 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("Filename") + file, _ := c.FormFile("file") log.Println(file.Filename) // Upload the file to specific dst. @@ -417,7 +417,7 @@ func main() { router.POST("/upload", func(c *gin.Context) { // Multipart form form, _ := c.MultipartForm() - files := form.File["Filename[]"] + files := form.File["upload[]"] for _, file := range files { log.Println(file.Filename) From 7927a45143da95dc4151519ceda2de78a7cd55c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Mar 2022 23:19:06 +0800 Subject: [PATCH 094/346] Bump actions/checkout from 2 to 3 (#3068) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 2 +- .github/workflows/gin.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4a081e0cd1..4cbc4554fb 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index f3927b3d46..5b818a9e24 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -17,7 +17,7 @@ jobs: with: go-version: '^1.16' - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup golangci-lint uses: golangci/golangci-lint-action@v2 with: @@ -48,7 +48,7 @@ jobs: go-version: ${{ matrix.go }} - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.ref }} From 90330e2a76a76686dc62d18ba55a4f63768c589b Mon Sep 17 00:00:00 2001 From: linzi <873804682@qq.com> Date: Thu, 17 Mar 2022 11:55:08 +0800 Subject: [PATCH 095/346] Distinguish between group and nested group (#3083) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d3fa075764..2d113532bb 100644 --- a/README.md +++ b/README.md @@ -513,6 +513,7 @@ func main() { // nested group testing := authorized.Group("testing") + // visit 0.0.0.0:8080/testing/analytics testing.GET("/analytics", analyticsEndpoint) } From 8860527de602b561500d68cb345c8b07de22805e Mon Sep 17 00:00:00 2001 From: metal A-wing Date: Thu, 17 Mar 2022 03:56:16 +0000 Subject: [PATCH 096/346] feat attachment filename support utf8 (#3071) --- context.go | 7 +++++-- context_test.go | 14 ++++++++++++++ utils.go | 11 +++++++++++ utils_test.go | 5 +++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index d69df70b24..48d7550697 100644 --- a/context.go +++ b/context.go @@ -6,7 +6,6 @@ package gin import ( "errors" - "fmt" "io" "io/ioutil" "log" @@ -1018,7 +1017,11 @@ func (c *Context) FileFromFS(filepath string, fs http.FileSystem) { // FileAttachment writes the specified file into the body stream in an efficient way // On the client side, the file will typically be downloaded with the given filename func (c *Context) FileAttachment(filepath, filename string) { - c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) + if isASCII(filename) { + c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`) + } else { + c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename)) + } http.ServeFile(c.Writer, c.Request, filepath) } diff --git a/context_test.go b/context_test.go index 9e02aede13..4eed164ab1 100644 --- a/context_test.go +++ b/context_test.go @@ -15,6 +15,7 @@ import ( "net" "net/http" "net/http/httptest" + "net/url" "os" "reflect" "strings" @@ -1033,6 +1034,19 @@ func TestContextRenderAttachment(t *testing.T) { assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition")) } +func TestContextRenderUTF8Attachment(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + newFilename := "new🧡_filename.go" + + c.Request, _ = http.NewRequest("GET", "/", nil) + c.FileAttachment("./gin.go", newFilename) + + assert.Equal(t, 200, w.Code) + assert.Contains(t, w.Body.String(), "func New() *Engine {") + assert.Equal(t, `attachment; filename*=UTF-8''`+url.QueryEscape(newFilename), w.Header().Get("Content-Disposition")) +} + // TestContextRenderYAML tests that the response is serialized as YAML // and Content-Type is set to application/x-yaml func TestContextRenderYAML(t *testing.T) { diff --git a/utils.go b/utils.go index c32f0eeb0e..e4599ea927 100644 --- a/utils.go +++ b/utils.go @@ -12,6 +12,7 @@ import ( "reflect" "runtime" "strings" + "unicode" ) // BindKey indicates a default bind key. @@ -151,3 +152,13 @@ func resolveAddress(addr []string) string { panic("too many parameters") } } + +// https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters +func isASCII(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] > unicode.MaxASCII { + return false + } + } + return true +} diff --git a/utils_test.go b/utils_test.go index b50914f25e..d2a740bfb1 100644 --- a/utils_test.go +++ b/utils_test.go @@ -143,3 +143,8 @@ func TestMarshalXMLforH(t *testing.T) { e := h.MarshalXML(enc, x) assert.Error(t, e) } + +func TestIsASCII(t *testing.T) { + assert.Equal(t, isASCII("test"), true) + assert.Equal(t, isASCII("🧡💛💚💙💜"), false) +} From 417b142703594c1a7dff030e67c38e1dfec9f1fc Mon Sep 17 00:00:00 2001 From: thinkgo <49174849+thinkgos@users.noreply.github.com> Date: Fri, 18 Mar 2022 09:52:23 +0800 Subject: [PATCH 097/346] feat: add StaticFileFS (#2749) * RouterGroup.StaticFileFS added add StaticFileFS ad README * fix Static content mistake * update README `tab` improve StaticFile and StaticFileFS code, use staticFileHandler --- README.md | 3 ++- routergroup.go | 19 ++++++++++++++++--- routergroup_test.go | 12 ++++++++++++ routes_test.go | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2d113532bb..4b4d236bbe 100644 --- a/README.md +++ b/README.md @@ -1244,7 +1244,8 @@ func main() { router.Static("/assets", "./assets") router.StaticFS("/more_static", http.Dir("my_file_system")) router.StaticFile("/favicon.ico", "./resources/favicon.ico") - + router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system")) + // Listen and serve on 0.0.0.0:8080 router.Run(":8080") } diff --git a/routergroup.go b/routergroup.go index b84fcca3d8..3fba3a9107 100644 --- a/routergroup.go +++ b/routergroup.go @@ -44,6 +44,7 @@ type IRoutes interface { HEAD(string, ...HandlerFunc) IRoutes StaticFile(string, string) IRoutes + StaticFileFS(string, string, http.FileSystem) IRoutes Static(string, string) IRoutes StaticFS(string, http.FileSystem) IRoutes } @@ -153,12 +154,24 @@ func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRou // StaticFile registers a single route in order to serve a single file of the local filesystem. // router.StaticFile("favicon.ico", "./resources/favicon.ico") func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes { + return group.staticFileHandler(relativePath, func(c *Context) { + c.File(filepath) + }) +} + +// StaticFileFS works just like `StaticFile` but a custom `http.FileSystem` can be used instead.. +// router.StaticFileFS("favicon.ico", "./resources/favicon.ico", Dir{".", false}) +// Gin by default user: gin.Dir() +func (group *RouterGroup) StaticFileFS(relativePath, filepath string, fs http.FileSystem) IRoutes { + return group.staticFileHandler(relativePath, func(c *Context) { + c.FileFromFS(filepath, fs) + }) +} + +func (group *RouterGroup) staticFileHandler(relativePath string, handler HandlerFunc) IRoutes { if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") { panic("URL parameters can not be used when serving a static file") } - handler := func(c *Context) { - c.File(filepath) - } group.GET(relativePath, handler) group.HEAD(relativePath, handler) return group.returnObj() diff --git a/routergroup_test.go b/routergroup_test.go index 232f595b0a..c1fad3a984 100644 --- a/routergroup_test.go +++ b/routergroup_test.go @@ -111,6 +111,17 @@ func TestRouterGroupInvalidStaticFile(t *testing.T) { }) } +func TestRouterGroupInvalidStaticFileFS(t *testing.T) { + router := New() + assert.Panics(t, func() { + router.StaticFileFS("/path/:param", "favicon.ico", Dir(".", false)) + }) + + assert.Panics(t, func() { + router.StaticFileFS("/path/*param", "favicon.ico", Dir(".", false)) + }) +} + func TestRouterGroupTooManyHandlers(t *testing.T) { const ( panicValue = "too many handlers" @@ -177,6 +188,7 @@ func testRoutesInterface(t *testing.T, r IRoutes) { assert.Equal(t, r, r.HEAD("/", handler)) assert.Equal(t, r, r.StaticFile("/file", ".")) + assert.Equal(t, r, r.StaticFileFS("/static2", ".", Dir(".", false))) assert.Equal(t, r, r.Static("/static", ".")) assert.Equal(t, r, r.StaticFS("/static2", Dir(".", false))) } diff --git a/routes_test.go b/routes_test.go index b3f0c47b88..4a0cb49378 100644 --- a/routes_test.go +++ b/routes_test.go @@ -325,6 +325,40 @@ func TestRouteStaticFile(t *testing.T) { assert.Equal(t, http.StatusOK, w3.Code) } +// TestHandleStaticFile - ensure the static file handles properly +func TestRouteStaticFileFS(t *testing.T) { + // SETUP file + testRoot, _ := os.Getwd() + f, err := ioutil.TempFile(testRoot, "") + if err != nil { + t.Error(err) + } + defer os.Remove(f.Name()) + _, err = f.WriteString("Gin Web Framework") + assert.NoError(t, err) + f.Close() + + dir, filename := filepath.Split(f.Name()) + // SETUP gin + router := New() + router.Static("/using_static", dir) + router.StaticFileFS("/result_fs", filename, Dir(dir, false)) + + w := performRequest(router, http.MethodGet, "/using_static/"+filename) + w2 := performRequest(router, http.MethodGet, "/result_fs") + + assert.Equal(t, w, w2) + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "Gin Web Framework", w.Body.String()) + assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) + + w3 := performRequest(router, http.MethodHead, "/using_static/"+filename) + w4 := performRequest(router, http.MethodHead, "/result_fs") + + assert.Equal(t, w3, w4) + assert.Equal(t, http.StatusOK, w3.Code) +} + // TestHandleStaticDir - ensure the root/sub dir handles properly func TestRouteStaticListingDir(t *testing.T) { router := New() From fcd36c549da01a72aef0fc6a176a27badb9e3fe9 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Fri, 18 Mar 2022 11:55:25 +0800 Subject: [PATCH 098/346] fix: test error (#3087) --- routes_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routes_test.go b/routes_test.go index 4a0cb49378..5643097069 100644 --- a/routes_test.go +++ b/routes_test.go @@ -344,16 +344,16 @@ func TestRouteStaticFileFS(t *testing.T) { router.Static("/using_static", dir) router.StaticFileFS("/result_fs", filename, Dir(dir, false)) - w := performRequest(router, http.MethodGet, "/using_static/"+filename) - w2 := performRequest(router, http.MethodGet, "/result_fs") + w := PerformRequest(router, http.MethodGet, "/using_static/"+filename) + w2 := PerformRequest(router, http.MethodGet, "/result_fs") assert.Equal(t, w, w2) assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "Gin Web Framework", w.Body.String()) assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) - w3 := performRequest(router, http.MethodHead, "/using_static/"+filename) - w4 := performRequest(router, http.MethodHead, "/result_fs") + w3 := PerformRequest(router, http.MethodHead, "/using_static/"+filename) + w4 := PerformRequest(router, http.MethodHead, "/result_fs") assert.Equal(t, w3, w4) assert.Equal(t, http.StatusOK, w3.Code) From f073e33fb93288aecd8bfda789f12c03e5bda829 Mon Sep 17 00:00:00 2001 From: a2tt Date: Fri, 18 Mar 2022 18:41:09 +0900 Subject: [PATCH 099/346] fix: typo (#3086) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b4d236bbe..9a9785e642 100644 --- a/README.md +++ b/README.md @@ -1411,7 +1411,7 @@ import ( func formatAsDate(t time.Time) string { year, month, day := t.Date() - return fmt.Sprintf("%d%02d/%02d", year, month, day) + return fmt.Sprintf("%d/%02d/%02d", year, month, day) } func main() { From d8dfaaeb2e3b6c1c6a5ac5ffe41d2e20be8f7958 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 19 Mar 2022 21:19:57 +0800 Subject: [PATCH 100/346] Bump golangci/golangci-lint-action from 2 to 3.1.0 (#3063) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 5b818a9e24..1aecda580b 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -19,7 +19,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Setup golangci-lint - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v3.1.0 with: version: v1.44.0 args: --verbose From 1e24473f5f6b80aff859826edc29f8ae87cca2ff Mon Sep 17 00:00:00 2001 From: Mike <38686456+icy4ever@users.noreply.github.com> Date: Sun, 20 Mar 2022 21:26:12 +0800 Subject: [PATCH 101/346] Annotation fix (#3088) * fix annotation * fix annotation --- tree.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 18e50ee363..88100eec84 100644 --- a/tree.go +++ b/tree.go @@ -325,7 +325,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) n.priority++ // if the path doesn't end with the wildcard, then there - // will be another non-wildcard subpath starting with '/' + // will be another subpath starting with '/' if len(wildcard) < len(path) { path = path[len(wildcard):] From 9701b651b7cf2a0e1d501674f6cc92d8b8ee81bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 20 Mar 2022 21:31:42 +0800 Subject: [PATCH 102/346] Bump github.com/ugorji/go/codec from 1.2.6 to 1.2.7 (#3064) Bumps [github.com/ugorji/go/codec](https://github.com/ugorji/go) from 1.2.6 to 1.2.7. - [Release notes](https://github.com/ugorji/go/releases) - [Commits](https://github.com/ugorji/go/compare/v1.2.6...v1.2.7) --- updated-dependencies: - dependency-name: github.com/ugorji/go/codec dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 8087227e67..7a9249c6e0 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/stretchr/testify v1.7.0 - github.com/ugorji/go/codec v1.2.6 + github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 7f892453b7..6006ffb989 100644 --- a/go.sum +++ b/go.sum @@ -47,10 +47,10 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= -github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= -github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= -github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= 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/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= From 2bde107686759098e2d64273bc79d1a0216a4500 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 21 Mar 2022 09:43:17 +0800 Subject: [PATCH 103/346] test support go1.18 (#2990) --- any.go | 10 +++ binding/any.go | 10 +++ binding/binding.go | 12 ++-- binding/binding_nomsgpack.go | 12 ++-- binding/binding_test.go | 18 +++--- binding/default_validator.go | 6 +- binding/default_validator_test.go | 2 +- binding/form.go | 6 +- binding/form_mapping.go | 14 ++-- binding/form_mapping_test.go | 4 +- binding/header.go | 4 +- binding/json.go | 6 +- binding/msgpack.go | 6 +- binding/msgpack_test.go | 2 +- binding/multipart_form_mapping_test.go | 2 +- binding/protobuf.go | 4 +- binding/query.go | 2 +- binding/uri.go | 2 +- binding/validate_test.go | 4 +- binding/xml.go | 6 +- binding/yaml.go | 6 +- context.go | 88 +++++++++++++------------- context_test.go | 16 ++--- debug.go | 2 +- deprecated.go | 2 +- errors.go | 10 +-- errors_test.go | 2 +- gin.go | 2 +- gin_test.go | 4 +- logger.go | 2 +- logger_test.go | 2 +- recovery.go | 4 +- recovery_test.go | 6 +- render/any.go | 10 +++ render/html.go | 8 +-- render/json.go | 14 ++-- render/msgpack.go | 4 +- render/protobuf.go | 2 +- render/render_msgpack_test.go | 2 +- render/render_test.go | 36 +++++------ render/text.go | 4 +- render/xml.go | 2 +- render/yaml.go | 2 +- testdata/protoexample/any.go | 10 +++ testdata/protoexample/test.pb.go | 6 +- tree_test.go | 2 +- utils.go | 8 +-- 47 files changed, 214 insertions(+), 174 deletions(-) create mode 100644 any.go create mode 100644 binding/any.go create mode 100644 render/any.go create mode 100644 testdata/protoexample/any.go diff --git a/any.go b/any.go new file mode 100644 index 0000000000..a0104dc899 --- /dev/null +++ b/any.go @@ -0,0 +1,10 @@ +// Copyright 2022 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build !go1.18 +// +build !go1.18 + +package gin + +type any = interface{} diff --git a/binding/any.go b/binding/any.go new file mode 100644 index 0000000000..1331d390dd --- /dev/null +++ b/binding/any.go @@ -0,0 +1,10 @@ +// Copyright 2022 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build !go1.18 +// +build !go1.18 + +package binding + +type any = interface{} diff --git a/binding/binding.go b/binding/binding.go index 0414a3453b..703a1cf87e 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -29,21 +29,21 @@ const ( // the form POST. type Binding interface { Name() string - Bind(*http.Request, interface{}) error + Bind(*http.Request, any) error } // BindingBody adds BindBody method to Binding. BindBody is similar with Bind, // but it reads the body from supplied bytes instead of req.Body. type BindingBody interface { Binding - BindBody([]byte, interface{}) error + BindBody([]byte, any) error } // BindingUri adds BindUri method to Binding. BindUri is similar with Bind, // but it reads the Params. type BindingUri interface { Name() string - BindUri(map[string][]string, interface{}) error + BindUri(map[string][]string, any) error } // StructValidator is the minimal interface which needs to be implemented in @@ -57,11 +57,11 @@ type StructValidator interface { // If the received type is a struct or pointer to a struct, the validation should be performed. // If the struct is not valid or the validation itself fails, a descriptive error should be returned. // Otherwise nil must be returned. - ValidateStruct(interface{}) error + ValidateStruct(any) error // Engine returns the underlying validator engine which powers the // StructValidator implementation. - Engine() interface{} + Engine() any } // Validator is the default validator which implements the StructValidator @@ -110,7 +110,7 @@ func Default(method, contentType string) Binding { } } -func validate(obj interface{}) error { +func validate(obj any) error { if Validator == nil { return nil } diff --git a/binding/binding_nomsgpack.go b/binding/binding_nomsgpack.go index f0b667b215..b3818549f3 100644 --- a/binding/binding_nomsgpack.go +++ b/binding/binding_nomsgpack.go @@ -27,21 +27,21 @@ const ( // the form POST. type Binding interface { Name() string - Bind(*http.Request, interface{}) error + Bind(*http.Request, any) error } // BindingBody adds BindBody method to Binding. BindBody is similar with Bind, // but it reads the body from supplied bytes instead of req.Body. type BindingBody interface { Binding - BindBody([]byte, interface{}) error + BindBody([]byte, any) error } // BindingUri adds BindUri method to Binding. BindUri is similar with Bind, // but it reads the Params. type BindingUri interface { Name() string - BindUri(map[string][]string, interface{}) error + BindUri(map[string][]string, any) error } // StructValidator is the minimal interface which needs to be implemented in @@ -54,11 +54,11 @@ type StructValidator interface { // If the received type is a struct or pointer to a struct, the validation should be performed. // If the struct is not valid or the validation itself fails, a descriptive error should be returned. // Otherwise nil must be returned. - ValidateStruct(interface{}) error + ValidateStruct(any) error // Engine returns the underlying validator engine which powers the // StructValidator implementation. - Engine() interface{} + Engine() any } // Validator is the default validator which implements the StructValidator @@ -104,7 +104,7 @@ func Default(method, contentType string) Binding { } } -func validate(obj interface{}) error { +func validate(obj any) error { if Validator == nil { return nil } diff --git a/binding/binding_test.go b/binding/binding_test.go index 5b0ce39d3e..b1edbf5a03 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -61,11 +61,11 @@ type FooDefaultBarStruct struct { } type FooStructUseNumber struct { - Foo interface{} `json:"foo" binding:"required"` + Foo any `json:"foo" binding:"required"` } type FooStructDisallowUnknownFields struct { - Foo interface{} `json:"foo" binding:"required"` + Foo any `json:"foo" binding:"required"` } type FooBarStructForTimeType struct { @@ -93,7 +93,7 @@ type FooStructForTimeTypeFailLocation struct { } type FooStructForMapType struct { - MapFoo map[string]interface{} `form:"map_foo"` + MapFoo map[string]any `form:"map_foo"` } type FooStructForIgnoreFormTag struct { @@ -106,7 +106,7 @@ type InvalidNameType struct { type InvalidNameMapType struct { TestName struct { - MapFoo map[string]interface{} `form:"map_foo"` + MapFoo map[string]any `form:"map_foo"` } } @@ -128,7 +128,7 @@ type FooStructForStructPointerType struct { type FooStructForSliceMapType struct { // Unknown type: not support map - SliceMapFoo []map[string]interface{} `form:"slice_map_foo"` + SliceMapFoo []map[string]any `form:"slice_map_foo"` } type FooStructForBoolType struct { @@ -141,7 +141,7 @@ type FooStructForStringPtrType struct { } type FooStructForMapPtrType struct { - PtrBar *map[string]interface{} `form:"ptr_bar"` + PtrBar *map[string]any `form:"ptr_bar"` } func TestBindingDefault(t *testing.T) { @@ -768,7 +768,7 @@ func TestHeaderBinding(t *testing.T) { req.Header.Add("fail", `{fail:fail}`) type failStruct struct { - Fail map[string]interface{} `header:"fail"` + Fail map[string]any `header:"fail"` } err := h.Bind(req, &failStruct{}) @@ -789,11 +789,11 @@ func TestUriBinding(t *testing.T) { assert.Equal(t, "thinkerou", tag.Name) type NotSupportStruct struct { - Name map[string]interface{} `uri:"name"` + Name map[string]any `uri:"name"` } var not NotSupportStruct assert.Error(t, b.BindUri(m, ¬)) - assert.Equal(t, map[string]interface{}(nil), not.Name) + assert.Equal(t, map[string]any(nil), not.Name) } func TestUriInnerBinding(t *testing.T) { diff --git a/binding/default_validator.go b/binding/default_validator.go index bd8764b194..3515a8cc57 100644 --- a/binding/default_validator.go +++ b/binding/default_validator.go @@ -46,7 +46,7 @@ func (err SliceValidationError) Error() string { var _ StructValidator = &defaultValidator{} // ValidateStruct receives any kind of type, but only performed struct or pointer to struct type. -func (v *defaultValidator) ValidateStruct(obj interface{}) error { +func (v *defaultValidator) ValidateStruct(obj any) error { if obj == nil { return nil } @@ -75,7 +75,7 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error { } // validateStruct receives struct type -func (v *defaultValidator) validateStruct(obj interface{}) error { +func (v *defaultValidator) validateStruct(obj any) error { v.lazyinit() return v.validate.Struct(obj) } @@ -84,7 +84,7 @@ func (v *defaultValidator) validateStruct(obj interface{}) error { // Validator instance. This is useful if you want to register custom validations // or struct level validations. See validator GoDoc for more info - // https://pkg.go.dev/github.com/go-playground/validator/v10 -func (v *defaultValidator) Engine() interface{} { +func (v *defaultValidator) Engine() any { v.lazyinit() return v.validate } diff --git a/binding/default_validator_test.go b/binding/default_validator_test.go index ff1301020e..df7742b755 100644 --- a/binding/default_validator_test.go +++ b/binding/default_validator_test.go @@ -54,7 +54,7 @@ func TestDefaultValidator(t *testing.T) { tests := []struct { name string v *defaultValidator - obj interface{} + obj any wantErr bool }{ {"validate nil obj", &defaultValidator{}, nil, false}, diff --git a/binding/form.go b/binding/form.go index fa2a6540a0..f5cbf57b2e 100644 --- a/binding/form.go +++ b/binding/form.go @@ -19,7 +19,7 @@ func (formBinding) Name() string { return "form" } -func (formBinding) Bind(req *http.Request, obj interface{}) error { +func (formBinding) Bind(req *http.Request, obj any) error { if err := req.ParseForm(); err != nil { return err } @@ -36,7 +36,7 @@ func (formPostBinding) Name() string { return "form-urlencoded" } -func (formPostBinding) Bind(req *http.Request, obj interface{}) error { +func (formPostBinding) Bind(req *http.Request, obj any) error { if err := req.ParseForm(); err != nil { return err } @@ -50,7 +50,7 @@ func (formMultipartBinding) Name() string { return "multipart/form-data" } -func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error { +func (formMultipartBinding) Bind(req *http.Request, obj any) error { if err := req.ParseMultipartForm(defaultMemory); err != nil { return err } diff --git a/binding/form_mapping.go b/binding/form_mapping.go index fa7ad1bc4e..c24dd553df 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -26,24 +26,24 @@ var ( ErrConvertToMapString = errors.New("can not convert to map of strings") ) -func mapURI(ptr interface{}, m map[string][]string) error { +func mapURI(ptr any, m map[string][]string) error { return mapFormByTag(ptr, m, "uri") } -func mapForm(ptr interface{}, form map[string][]string) error { +func mapForm(ptr any, form map[string][]string) error { return mapFormByTag(ptr, form, "form") } -func MapFormWithTag(ptr interface{}, form map[string][]string, tag string) error { +func MapFormWithTag(ptr any, form map[string][]string, tag string) error { return mapFormByTag(ptr, form, tag) } var emptyField = reflect.StructField{} -func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error { +func mapFormByTag(ptr any, form map[string][]string, tag string) error { // Check if ptr is a map ptrVal := reflect.ValueOf(ptr) - var pointed interface{} + var pointed any if ptrVal.Kind() == reflect.Ptr { ptrVal = ptrVal.Elem() pointed = ptrVal.Interface() @@ -73,7 +73,7 @@ func (form formSource) TrySet(value reflect.Value, field reflect.StructField, ta return setByForm(value, field, form, tagValue, opt) } -func mappingByPtr(ptr interface{}, setter setter, tag string) error { +func mappingByPtr(ptr any, setter setter, tag string) error { _, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag) return err } @@ -376,7 +376,7 @@ func head(str, sep string) (head string, tail string) { return str[:idx], str[idx+len(sep):] } -func setFormMap(ptr interface{}, form map[string][]string) error { +func setFormMap(ptr any, form map[string][]string) error { el := reflect.TypeOf(ptr).Elem() if el.Kind() == reflect.Slice { diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 516554eb7d..78f4df0e44 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -18,9 +18,9 @@ func TestMappingBaseTypes(t *testing.T) { } for _, tt := range []struct { name string - value interface{} + value any form string - expect interface{} + expect any }{ {"base type", struct{ F int }{}, "9", int(9)}, {"base type", struct{ F int8 }{}, "9", int8(9)}, diff --git a/binding/header.go b/binding/header.go index b99302af82..14f525ef5f 100644 --- a/binding/header.go +++ b/binding/header.go @@ -12,7 +12,7 @@ func (headerBinding) Name() string { return "header" } -func (headerBinding) Bind(req *http.Request, obj interface{}) error { +func (headerBinding) Bind(req *http.Request, obj any) error { if err := mapHeader(obj, req.Header); err != nil { return err @@ -21,7 +21,7 @@ func (headerBinding) Bind(req *http.Request, obj interface{}) error { return validate(obj) } -func mapHeader(ptr interface{}, h map[string][]string) error { +func mapHeader(ptr any, h map[string][]string) error { return mappingByPtr(ptr, headerSource(h), "header") } diff --git a/binding/json.go b/binding/json.go index 45aaa49487..2e3e1dd5c3 100644 --- a/binding/json.go +++ b/binding/json.go @@ -30,18 +30,18 @@ func (jsonBinding) Name() string { return "json" } -func (jsonBinding) Bind(req *http.Request, obj interface{}) error { +func (jsonBinding) Bind(req *http.Request, obj any) error { if req == nil || req.Body == nil { return errors.New("invalid request") } return decodeJSON(req.Body, obj) } -func (jsonBinding) BindBody(body []byte, obj interface{}) error { +func (jsonBinding) BindBody(body []byte, obj any) error { return decodeJSON(bytes.NewReader(body), obj) } -func decodeJSON(r io.Reader, obj interface{}) error { +func decodeJSON(r io.Reader, obj any) error { decoder := json.NewDecoder(r) if EnableDecoderUseNumber { decoder.UseNumber() diff --git a/binding/msgpack.go b/binding/msgpack.go index 2a442996a6..65197713e6 100644 --- a/binding/msgpack.go +++ b/binding/msgpack.go @@ -21,15 +21,15 @@ func (msgpackBinding) Name() string { return "msgpack" } -func (msgpackBinding) Bind(req *http.Request, obj interface{}) error { +func (msgpackBinding) Bind(req *http.Request, obj any) error { return decodeMsgPack(req.Body, obj) } -func (msgpackBinding) BindBody(body []byte, obj interface{}) error { +func (msgpackBinding) BindBody(body []byte, obj any) error { return decodeMsgPack(bytes.NewReader(body), obj) } -func decodeMsgPack(r io.Reader, obj interface{}) error { +func decodeMsgPack(r io.Reader, obj any) error { cdc := new(codec.MsgpackHandle) if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil { return err diff --git a/binding/msgpack_test.go b/binding/msgpack_test.go index 75600ba830..11561c843d 100644 --- a/binding/msgpack_test.go +++ b/binding/msgpack_test.go @@ -26,7 +26,7 @@ func TestMsgpackBindingBindBody(t *testing.T) { assert.Equal(t, "FOO", s.Foo) } -func msgpackBody(t *testing.T, obj interface{}) []byte { +func msgpackBody(t *testing.T, obj any) []byte { var bs bytes.Buffer h := &codec.MsgpackHandle{} err := codec.NewEncoder(&bs, h).Encode(obj) diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go index 4aaa60be98..545914d054 100644 --- a/binding/multipart_form_mapping_test.go +++ b/binding/multipart_form_mapping_test.go @@ -76,7 +76,7 @@ func TestFormMultipartBindingBindError(t *testing.T) { for _, tt := range []struct { name string - s interface{} + s any }{ {"wrong type", &struct { Files int `form:"file"` diff --git a/binding/protobuf.go b/binding/protobuf.go index a4e471535c..ace8f6547d 100644 --- a/binding/protobuf.go +++ b/binding/protobuf.go @@ -18,7 +18,7 @@ func (protobufBinding) Name() string { return "protobuf" } -func (b protobufBinding) Bind(req *http.Request, obj interface{}) error { +func (b protobufBinding) Bind(req *http.Request, obj any) error { buf, err := ioutil.ReadAll(req.Body) if err != nil { return err @@ -26,7 +26,7 @@ func (b protobufBinding) Bind(req *http.Request, obj interface{}) error { return b.BindBody(buf, obj) } -func (protobufBinding) BindBody(body []byte, obj interface{}) error { +func (protobufBinding) BindBody(body []byte, obj any) error { msg, ok := obj.(proto.Message) if !ok { return errors.New("obj is not ProtoMessage") diff --git a/binding/query.go b/binding/query.go index 219743f2a9..9790ce6a26 100644 --- a/binding/query.go +++ b/binding/query.go @@ -12,7 +12,7 @@ func (queryBinding) Name() string { return "query" } -func (queryBinding) Bind(req *http.Request, obj interface{}) error { +func (queryBinding) Bind(req *http.Request, obj any) error { values := req.URL.Query() if err := mapForm(obj, values); err != nil { return err diff --git a/binding/uri.go b/binding/uri.go index a3c0df515c..4d44ab98b8 100644 --- a/binding/uri.go +++ b/binding/uri.go @@ -10,7 +10,7 @@ func (uriBinding) Name() string { return "uri" } -func (uriBinding) BindUri(m map[string][]string, obj interface{}) error { +func (uriBinding) BindUri(m map[string][]string, obj any) error { if err := mapURI(obj, m); err != nil { return err } diff --git a/binding/validate_test.go b/binding/validate_test.go index 5299fbf602..d05c891ea1 100644 --- a/binding/validate_test.go +++ b/binding/validate_test.go @@ -59,7 +59,7 @@ type structNoValidationValues struct { StructSlice []substructNoValidation InterfaceSlice []testInterface - UniversalInterface interface{} + UniversalInterface any CustomInterface testInterface FloatMap map[string]float32 @@ -169,7 +169,7 @@ func TestValidateNoValidationPointers(t *testing.T) { //assert.Equal(t, origin, test) } -type Object map[string]interface{} +type Object map[string]any func TestValidatePrimitives(t *testing.T) { obj := Object{"foo": "bar", "bar": 1} diff --git a/binding/xml.go b/binding/xml.go index 4e90114962..e62af4a679 100644 --- a/binding/xml.go +++ b/binding/xml.go @@ -17,14 +17,14 @@ func (xmlBinding) Name() string { return "xml" } -func (xmlBinding) Bind(req *http.Request, obj interface{}) error { +func (xmlBinding) Bind(req *http.Request, obj any) error { return decodeXML(req.Body, obj) } -func (xmlBinding) BindBody(body []byte, obj interface{}) error { +func (xmlBinding) BindBody(body []byte, obj any) error { return decodeXML(bytes.NewReader(body), obj) } -func decodeXML(r io.Reader, obj interface{}) error { +func decodeXML(r io.Reader, obj any) error { decoder := xml.NewDecoder(r) if err := decoder.Decode(obj); err != nil { return err diff --git a/binding/yaml.go b/binding/yaml.go index a2d36d6a54..183f141ed6 100644 --- a/binding/yaml.go +++ b/binding/yaml.go @@ -18,15 +18,15 @@ func (yamlBinding) Name() string { return "yaml" } -func (yamlBinding) Bind(req *http.Request, obj interface{}) error { +func (yamlBinding) Bind(req *http.Request, obj any) error { return decodeYAML(req.Body, obj) } -func (yamlBinding) BindBody(body []byte, obj interface{}) error { +func (yamlBinding) BindBody(body []byte, obj any) error { return decodeYAML(bytes.NewReader(body), obj) } -func decodeYAML(r io.Reader, obj interface{}) error { +func decodeYAML(r io.Reader, obj any) error { decoder := yaml.NewDecoder(r) if err := decoder.Decode(obj); err != nil { return err diff --git a/context.go b/context.go index 48d7550697..faa481332a 100644 --- a/context.go +++ b/context.go @@ -62,7 +62,7 @@ type Context struct { mu sync.RWMutex // Keys is a key/value pair exclusively for the context of each request. - Keys map[string]interface{} + Keys map[string]any // Errors is a list of errors attached to all the handlers/middlewares who used this context. Errors errorMsgs @@ -115,7 +115,7 @@ func (c *Context) Copy() *Context { cp.Writer = &cp.writermem cp.index = abortIndex cp.handlers = nil - cp.Keys = map[string]interface{}{} + cp.Keys = map[string]any{} for k, v := range c.Keys { cp.Keys[k] = v } @@ -194,7 +194,7 @@ func (c *Context) AbortWithStatus(code int) { // AbortWithStatusJSON calls `Abort()` and then `JSON` internally. // This method stops the chain, writes the status code and return a JSON body. // It also sets the Content-Type as "application/json". -func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) { +func (c *Context) AbortWithStatusJSON(code int, jsonObj any) { c.Abort() c.JSON(code, jsonObj) } @@ -240,10 +240,10 @@ func (c *Context) Error(err error) *Error { // Set is used to store a new key/value pair exclusively for this context. // It also lazy initializes c.Keys if it was not used previously. -func (c *Context) Set(key string, value interface{}) { +func (c *Context) Set(key string, value any) { c.mu.Lock() if c.Keys == nil { - c.Keys = make(map[string]interface{}) + c.Keys = make(map[string]any) } c.Keys[key] = value @@ -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 exist it returns (nil, false) -func (c *Context) Get(key string) (value interface{}, exists bool) { +func (c *Context) Get(key string) (value any, exists bool) { c.mu.RLock() value, exists = c.Keys[key] c.mu.RUnlock() @@ -260,7 +260,7 @@ func (c *Context) Get(key string) (value interface{}, exists bool) { } // MustGet returns the value for the given key if it exists, otherwise it panics. -func (c *Context) MustGet(key string) interface{} { +func (c *Context) MustGet(key string) any { if value, exists := c.Get(key); exists { return value } @@ -348,9 +348,9 @@ func (c *Context) GetStringSlice(key string) (ss []string) { } // GetStringMap returns the value associated with the key as a map of interfaces. -func (c *Context) GetStringMap(key string) (sm map[string]interface{}) { +func (c *Context) GetStringMap(key string) (sm map[string]any) { if val, ok := c.Get(key); ok && val != nil { - sm, _ = val.(map[string]interface{}) + sm, _ = val.(map[string]any) } return } @@ -607,39 +607,39 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It decodes the json payload into the struct specified as a pointer. // It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid. -func (c *Context) Bind(obj interface{}) error { +func (c *Context) Bind(obj any) error { b := binding.Default(c.Request.Method, c.ContentType()) return c.MustBindWith(obj, b) } // BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON). -func (c *Context) BindJSON(obj interface{}) error { +func (c *Context) BindJSON(obj any) error { return c.MustBindWith(obj, binding.JSON) } // BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML). -func (c *Context) BindXML(obj interface{}) error { +func (c *Context) BindXML(obj any) error { return c.MustBindWith(obj, binding.XML) } // BindQuery is a shortcut for c.MustBindWith(obj, binding.Query). -func (c *Context) BindQuery(obj interface{}) error { +func (c *Context) BindQuery(obj any) error { return c.MustBindWith(obj, binding.Query) } // BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML). -func (c *Context) BindYAML(obj interface{}) error { +func (c *Context) BindYAML(obj any) error { return c.MustBindWith(obj, binding.YAML) } // BindHeader is a shortcut for c.MustBindWith(obj, binding.Header). -func (c *Context) BindHeader(obj interface{}) error { +func (c *Context) BindHeader(obj any) error { return c.MustBindWith(obj, binding.Header) } // BindUri binds the passed struct pointer using binding.Uri. // It will abort the request with HTTP 400 if any error occurs. -func (c *Context) BindUri(obj interface{}) error { +func (c *Context) BindUri(obj any) error { if err := c.ShouldBindUri(obj); err != nil { c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck return err @@ -650,7 +650,7 @@ func (c *Context) BindUri(obj interface{}) error { // MustBindWith binds the passed struct pointer using the specified binding engine. // It will abort the request with HTTP 400 if any error occurs. // See the binding package. -func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error { +func (c *Context) MustBindWith(obj any, b binding.Binding) error { if err := c.ShouldBindWith(obj, b); err != nil { c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck return err @@ -665,38 +665,38 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error { // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It decodes the json payload into the struct specified as a pointer. // Like c.Bind() but this method does not set the response status code to 400 or abort if input is not valid. -func (c *Context) ShouldBind(obj interface{}) error { +func (c *Context) ShouldBind(obj any) error { b := binding.Default(c.Request.Method, c.ContentType()) return c.ShouldBindWith(obj, b) } // ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON). -func (c *Context) ShouldBindJSON(obj interface{}) error { +func (c *Context) ShouldBindJSON(obj any) error { return c.ShouldBindWith(obj, binding.JSON) } // ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML). -func (c *Context) ShouldBindXML(obj interface{}) error { +func (c *Context) ShouldBindXML(obj any) error { return c.ShouldBindWith(obj, binding.XML) } // ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query). -func (c *Context) ShouldBindQuery(obj interface{}) error { +func (c *Context) ShouldBindQuery(obj any) error { return c.ShouldBindWith(obj, binding.Query) } // ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML). -func (c *Context) ShouldBindYAML(obj interface{}) error { +func (c *Context) ShouldBindYAML(obj any) error { return c.ShouldBindWith(obj, binding.YAML) } // ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header). -func (c *Context) ShouldBindHeader(obj interface{}) error { +func (c *Context) ShouldBindHeader(obj any) error { return c.ShouldBindWith(obj, binding.Header) } // ShouldBindUri binds the passed struct pointer using the specified binding engine. -func (c *Context) ShouldBindUri(obj interface{}) error { +func (c *Context) ShouldBindUri(obj any) error { m := make(map[string][]string) for _, v := range c.Params { m[v.Key] = []string{v.Value} @@ -706,7 +706,7 @@ func (c *Context) ShouldBindUri(obj interface{}) error { // ShouldBindWith binds the passed struct pointer using the specified binding engine. // See the binding package. -func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error { +func (c *Context) ShouldBindWith(obj any, b binding.Binding) error { return b.Bind(c.Request, obj) } @@ -715,7 +715,7 @@ func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error { // // NOTE: This method reads the body before binding. So you should use // ShouldBindWith for better performance if you need to call only once. -func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) { +func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error) { var body []byte if cb, ok := c.Get(BodyBytesKey); ok { if cbb, ok := cb.([]byte); ok { @@ -900,7 +900,7 @@ func (c *Context) Render(code int, r render.Render) { // HTML renders the HTTP template specified by its file name. // It also updates the HTTP code and sets the Content-Type as "text/html". // See http://golang.org/doc/articles/wiki/ -func (c *Context) HTML(code int, name string, obj interface{}) { +func (c *Context) HTML(code int, name string, obj any) { instance := c.engine.HTMLRender.Instance(name, obj) c.Render(code, instance) } @@ -909,21 +909,21 @@ func (c *Context) HTML(code int, name string, obj interface{}) { // It also sets the Content-Type as "application/json". // 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{}) { +func (c *Context) IndentedJSON(code int, obj any) { c.Render(code, render.IndentedJSON{Data: obj}) } // SecureJSON serializes the given struct as Secure JSON into the response body. // Default prepends "while(1)," to response body if the given struct is array values. // It also sets the Content-Type as "application/json". -func (c *Context) SecureJSON(code int, obj interface{}) { +func (c *Context) SecureJSON(code int, obj any) { c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj}) } // JSONP serializes the given struct as JSON into the response body. // It adds padding to response body to request data from a server residing in a different domain than the client. // It also sets the Content-Type as "application/javascript". -func (c *Context) JSONP(code int, obj interface{}) { +func (c *Context) JSONP(code int, obj any) { callback := c.DefaultQuery("callback", "") if callback == "" { c.Render(code, render.JSON{Data: obj}) @@ -934,40 +934,40 @@ func (c *Context) JSONP(code int, obj interface{}) { // JSON serializes the given struct as JSON into the response body. // It also sets the Content-Type as "application/json". -func (c *Context) JSON(code int, obj interface{}) { +func (c *Context) JSON(code int, obj any) { c.Render(code, render.JSON{Data: obj}) } // AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. // It also sets the Content-Type as "application/json". -func (c *Context) AsciiJSON(code int, obj interface{}) { +func (c *Context) AsciiJSON(code int, obj any) { c.Render(code, render.AsciiJSON{Data: obj}) } // PureJSON serializes the given struct as JSON into the response body. // PureJSON, unlike JSON, does not replace special html characters with their unicode entities. -func (c *Context) PureJSON(code int, obj interface{}) { +func (c *Context) PureJSON(code int, obj any) { c.Render(code, render.PureJSON{Data: obj}) } // XML serializes the given struct as XML into the response body. // It also sets the Content-Type as "application/xml". -func (c *Context) XML(code int, obj interface{}) { +func (c *Context) XML(code int, obj any) { c.Render(code, render.XML{Data: obj}) } // YAML serializes the given struct as YAML into the response body. -func (c *Context) YAML(code int, obj interface{}) { +func (c *Context) YAML(code int, obj any) { c.Render(code, render.YAML{Data: obj}) } // ProtoBuf serializes the given struct as ProtoBuf into the response body. -func (c *Context) ProtoBuf(code int, obj interface{}) { +func (c *Context) ProtoBuf(code int, obj any) { c.Render(code, render.ProtoBuf{Data: obj}) } // String writes the given string into the response body. -func (c *Context) String(code int, format string, values ...interface{}) { +func (c *Context) String(code int, format string, values ...any) { c.Render(code, render.String{Format: format, Data: values}) } @@ -1026,7 +1026,7 @@ func (c *Context) FileAttachment(filepath, filename string) { } // SSEvent writes a Server-Sent Event into the body stream. -func (c *Context) SSEvent(name string, message interface{}) { +func (c *Context) SSEvent(name string, message any) { c.Render(-1, sse.Event{ Event: name, Data: message, @@ -1060,11 +1060,11 @@ func (c *Context) Stream(step func(w io.Writer) bool) bool { type Negotiate struct { Offered []string HTMLName string - HTMLData interface{} - JSONData interface{} - XMLData interface{} - YAMLData interface{} - Data interface{} + HTMLData any + JSONData any + XMLData any + YAMLData any + Data any } // Negotiate calls different Render according to acceptable Accept format. @@ -1158,7 +1158,7 @@ func (c *Context) Err() error { // Value returns the value associated with this context for key, or nil // if no value is associated with key. Successive calls to Value with // the same key returns the same result. -func (c *Context) Value(key interface{}) interface{} { +func (c *Context) Value(key any) any { if key == 0 { return c.Request } diff --git a/context_test.go b/context_test.go index 4eed164ab1..fb46e6792f 100644 --- a/context_test.go +++ b/context_test.go @@ -212,7 +212,7 @@ func TestContextSetGetValues(t *testing.T) { c.Set("uint64", uint64(42)) c.Set("float32", float32(4.2)) c.Set("float64", 4.2) - var a interface{} = 1 + var a any = 1 c.Set("intInterface", a) assert.Exactly(t, c.MustGet("string").(string), "this is a string") @@ -287,7 +287,7 @@ func TestContextGetStringSlice(t *testing.T) { func TestContextGetStringMap(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - m := make(map[string]interface{}) + m := make(map[string]any) m["foo"] = 1 c.Set("map", m) @@ -2125,12 +2125,12 @@ type contextKey string func TestContextWithFallbackValueFromRequestContext(t *testing.T) { tests := []struct { name string - getContextAndKey func() (*Context, interface{}) - value interface{} + getContextAndKey func() (*Context, any) + value any }{ { name: "c with struct context key", - getContextAndKey: func() (*Context, interface{}) { + getContextAndKey: func() (*Context, any) { var key struct{} c := &Context{} c.Request, _ = http.NewRequest("POST", "/", nil) @@ -2141,7 +2141,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { }, { name: "c with string context key", - getContextAndKey: func() (*Context, interface{}) { + getContextAndKey: func() (*Context, any) { c := &Context{} c.Request, _ = http.NewRequest("POST", "/", nil) c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value")) @@ -2151,7 +2151,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { }, { name: "c with nil http.Request", - getContextAndKey: func() (*Context, interface{}) { + getContextAndKey: func() (*Context, any) { c := &Context{} return c, "key" }, @@ -2159,7 +2159,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { }, { name: "c with nil http.Request.Context()", - getContextAndKey: func() (*Context, interface{}) { + getContextAndKey: func() (*Context, any) { c := &Context{} c.Request, _ = http.NewRequest("POST", "/", nil) return c, "key" diff --git a/debug.go b/debug.go index ed313868e9..badcb5e1a3 100644 --- a/debug.go +++ b/debug.go @@ -47,7 +47,7 @@ func debugPrintLoadTemplate(tmpl *template.Template) { } } -func debugPrint(format string, values ...interface{}) { +func debugPrint(format string, values ...any) { if IsDebugging() { if !strings.HasSuffix(format, "\n") { format += "\n" diff --git a/deprecated.go b/deprecated.go index ab4474296e..004e13dbb6 100644 --- a/deprecated.go +++ b/deprecated.go @@ -12,7 +12,7 @@ import ( // BindWith binds the passed struct pointer using the specified binding engine. // See the binding package. -func (c *Context) BindWith(obj interface{}, b binding.Binding) error { +func (c *Context) BindWith(obj any, b binding.Binding) error { log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to be deprecated, please check issue #662 and either use MustBindWith() if you want HTTP 400 to be automatically returned if any error occur, or use diff --git a/errors.go b/errors.go index 3418cbc806..578e857d6a 100644 --- a/errors.go +++ b/errors.go @@ -34,7 +34,7 @@ const ( type Error struct { Err error Type ErrorType - Meta interface{} + Meta any } type errorMsgs []*Error @@ -48,13 +48,13 @@ func (msg *Error) SetType(flags ErrorType) *Error { } // SetMeta sets the error's meta data. -func (msg *Error) SetMeta(data interface{}) *Error { +func (msg *Error) SetMeta(data any) *Error { msg.Meta = data return msg } // JSON creates a properly formatted JSON -func (msg *Error) JSON() interface{} { +func (msg *Error) JSON() any { jsonData := H{} if msg.Meta != nil { value := reflect.ValueOf(msg.Meta) @@ -139,14 +139,14 @@ func (a errorMsgs) Errors() []string { return errorStrings } -func (a errorMsgs) JSON() interface{} { +func (a errorMsgs) JSON() any { switch length := len(a); length { case 0: return nil case 1: return a.Last().JSON() default: - jsonData := make([]interface{}, length) + jsonData := make([]any, length) for i, err := range a { jsonData[i] = err.JSON() } diff --git a/errors_test.go b/errors_test.go index 9a17d8594e..ac72dc42c9 100644 --- a/errors_test.go +++ b/errors_test.go @@ -86,7 +86,7 @@ Error #02: second Error #03: third Meta: map[status:400] `, errs.String()) - assert.Equal(t, []interface{}{ + assert.Equal(t, []any{ H{"error": "first"}, H{"error": "second", "meta": "some data"}, H{"error": "third", "status": "400"}, diff --git a/gin.go b/gin.go index b54dbd8a62..b0e0154fac 100644 --- a/gin.go +++ b/gin.go @@ -198,7 +198,7 @@ func New() *Engine { trustedCIDRs: defaultTrustedCIDRs, } engine.RouterGroup.engine = engine - engine.pool.New = func() interface{} { + engine.pool.New = func() any { return engine.allocateContext() } return engine diff --git a/gin_test.go b/gin_test.go index ae1762efd7..0c11134f04 100644 --- a/gin_test.go +++ b/gin_test.go @@ -43,7 +43,7 @@ func setupHTMLFiles(t *testing.T, mode string, tls bool, loadMethod func(*Engine c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"}) }) router.GET("/raw", func(c *Context) { - c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ + c.HTML(http.StatusOK, "raw.tmpl", map[string]any{ "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), }) }) @@ -467,7 +467,7 @@ func TestNoMethodWithGlobalHandlers(t *testing.T) { compareFunc(t, router.allNoMethod[2], middleware0) } -func compareFunc(t *testing.T, a, b interface{}) { +func compareFunc(t *testing.T, a, b any) { sf1 := reflect.ValueOf(a) sf2 := reflect.ValueOf(b) if sf1.Pointer() != sf2.Pointer() { diff --git a/logger.go b/logger.go index 61b84546d8..ab43d1123b 100644 --- a/logger.go +++ b/logger.go @@ -75,7 +75,7 @@ type LogFormatterParams struct { // BodySize is the size of the Response Body BodySize int // Keys are the keys set on the request's context. - Keys map[string]interface{} + Keys map[string]any } // StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal. diff --git a/logger_test.go b/logger_test.go index ec5e6cdccd..da1b654ec1 100644 --- a/logger_test.go +++ b/logger_test.go @@ -181,7 +181,7 @@ func TestLoggerWithFormatter(t *testing.T) { func TestLoggerWithConfigFormatting(t *testing.T) { var gotParam LogFormatterParams - var gotKeys map[string]interface{} + var gotKeys map[string]any buffer := new(bytes.Buffer) router := New() diff --git a/recovery.go b/recovery.go index 40eba3b202..3efe146b14 100644 --- a/recovery.go +++ b/recovery.go @@ -28,7 +28,7 @@ var ( ) // RecoveryFunc defines the function passable to CustomRecovery. -type RecoveryFunc func(c *Context, err interface{}) +type RecoveryFunc func(c *Context, err any) // Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. func Recovery() HandlerFunc { @@ -102,7 +102,7 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc { } } -func defaultHandleRecovery(c *Context, err interface{}) { +func defaultHandleRecovery(c *Context, err any) { c.AbortWithStatus(http.StatusInternalServerError) } diff --git a/recovery_test.go b/recovery_test.go index ac3669a419..2c327a6586 100644 --- a/recovery_test.go +++ b/recovery_test.go @@ -148,7 +148,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) { errBuffer := new(bytes.Buffer) buffer := new(bytes.Buffer) router := New() - handleRecovery := func(c *Context, err interface{}) { + handleRecovery := func(c *Context, err any) { errBuffer.WriteString(err.(string)) c.AbortWithStatus(http.StatusBadRequest) } @@ -183,7 +183,7 @@ func TestCustomRecovery(t *testing.T) { buffer := new(bytes.Buffer) router := New() DefaultErrorWriter = buffer - handleRecovery := func(c *Context, err interface{}) { + handleRecovery := func(c *Context, err any) { errBuffer.WriteString(err.(string)) c.AbortWithStatus(http.StatusBadRequest) } @@ -218,7 +218,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) { buffer := new(bytes.Buffer) router := New() DefaultErrorWriter = buffer - handleRecovery := func(c *Context, err interface{}) { + handleRecovery := func(c *Context, err any) { errBuffer.WriteString(err.(string)) c.AbortWithStatus(http.StatusBadRequest) } diff --git a/render/any.go b/render/any.go new file mode 100644 index 0000000000..57349be316 --- /dev/null +++ b/render/any.go @@ -0,0 +1,10 @@ +// Copyright 2021 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build !go1.18 +// +build !go1.18 + +package render + +type any = interface{} diff --git a/render/html.go b/render/html.go index 6696ece997..bdfaf11ace 100644 --- a/render/html.go +++ b/render/html.go @@ -20,7 +20,7 @@ type Delims struct { // HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug. type HTMLRender interface { // Instance returns an HTML instance. - Instance(string, interface{}) Render + Instance(string, any) Render } // HTMLProduction contains template reference and its delims. @@ -41,13 +41,13 @@ type HTMLDebug struct { type HTML struct { Template *template.Template Name string - Data interface{} + Data any } var htmlContentType = []string{"text/html; charset=utf-8"} // Instance (HTMLProduction) returns an HTML instance which it realizes Render interface. -func (r HTMLProduction) Instance(name string, data interface{}) Render { +func (r HTMLProduction) Instance(name string, data any) Render { return HTML{ Template: r.Template, Name: name, @@ -56,7 +56,7 @@ func (r HTMLProduction) Instance(name string, data interface{}) Render { } // Instance (HTMLDebug) returns an HTML instance which it realizes Render interface. -func (r HTMLDebug) Instance(name string, data interface{}) Render { +func (r HTMLDebug) Instance(name string, data any) Render { return HTML{ Template: r.loadTemplate(), Name: name, diff --git a/render/json.go b/render/json.go index 3ebcee9706..0a7dcef290 100644 --- a/render/json.go +++ b/render/json.go @@ -16,34 +16,34 @@ import ( // JSON contains the given interface object. type JSON struct { - Data interface{} + Data any } // IndentedJSON contains the given interface object. type IndentedJSON struct { - Data interface{} + Data any } // SecureJSON contains the given interface object and its prefix. type SecureJSON struct { Prefix string - Data interface{} + Data any } // JsonpJSON contains the given interface object its callback. type JsonpJSON struct { Callback string - Data interface{} + Data any } // AsciiJSON contains the given interface object. type AsciiJSON struct { - Data interface{} + Data any } // PureJSON contains the given interface object. type PureJSON struct { - Data interface{} + Data any } var ( @@ -66,7 +66,7 @@ func (r JSON) WriteContentType(w http.ResponseWriter) { } // WriteJSON marshals the given interface object and writes it with custom ContentType. -func WriteJSON(w http.ResponseWriter, obj interface{}) error { +func WriteJSON(w http.ResponseWriter, obj any) error { writeContentType(w, jsonContentType) jsonBytes, err := json.Marshal(obj) if err != nil { diff --git a/render/msgpack.go b/render/msgpack.go index 7f17ca4d9c..2c0e9aa8d0 100644 --- a/render/msgpack.go +++ b/render/msgpack.go @@ -21,7 +21,7 @@ var ( // MsgPack contains the given interface object. type MsgPack struct { - Data interface{} + Data any } var msgpackContentType = []string{"application/msgpack; charset=utf-8"} @@ -37,7 +37,7 @@ func (r MsgPack) Render(w http.ResponseWriter) error { } // WriteMsgPack writes MsgPack ContentType and encodes the given interface object. -func WriteMsgPack(w http.ResponseWriter, obj interface{}) error { +func WriteMsgPack(w http.ResponseWriter, obj any) error { writeContentType(w, msgpackContentType) var mh codec.MsgpackHandle return codec.NewEncoder(w, &mh).Encode(obj) diff --git a/render/protobuf.go b/render/protobuf.go index 1d2aa871c7..2e57f5ca0f 100644 --- a/render/protobuf.go +++ b/render/protobuf.go @@ -12,7 +12,7 @@ import ( // ProtoBuf contains the given interface object. type ProtoBuf struct { - Data interface{} + Data any } var protobufContentType = []string{"application/x-protobuf"} diff --git a/render/render_msgpack_test.go b/render/render_msgpack_test.go index d16cf6e6e2..7b3601a0fb 100644 --- a/render/render_msgpack_test.go +++ b/render/render_msgpack_test.go @@ -21,7 +21,7 @@ import ( func TestRenderMsgPack(t *testing.T) { w := httptest.NewRecorder() - data := map[string]interface{}{ + data := map[string]any{ "foo": "bar", } diff --git a/render/render_test.go b/render/render_test.go index e417731a89..8b28dc3f0e 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -24,7 +24,7 @@ import ( func TestRenderJSON(t *testing.T) { w := httptest.NewRecorder() - data := map[string]interface{}{ + data := map[string]any{ "foo": "bar", "html": "", } @@ -49,7 +49,7 @@ func TestRenderJSONPanics(t *testing.T) { func TestRenderIndentedJSON(t *testing.T) { w := httptest.NewRecorder() - data := map[string]interface{}{ + data := map[string]any{ "foo": "bar", "bar": "foo", } @@ -72,7 +72,7 @@ func TestRenderIndentedJSONPanics(t *testing.T) { func TestRenderSecureJSON(t *testing.T) { w1 := httptest.NewRecorder() - data := map[string]interface{}{ + data := map[string]any{ "foo": "bar", } @@ -86,7 +86,7 @@ func TestRenderSecureJSON(t *testing.T) { assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type")) w2 := httptest.NewRecorder() - datas := []map[string]interface{}{{ + datas := []map[string]any{{ "foo": "bar", }, { "bar": "foo", @@ -109,7 +109,7 @@ func TestRenderSecureJSONFail(t *testing.T) { func TestRenderJsonpJSON(t *testing.T) { w1 := httptest.NewRecorder() - data := map[string]interface{}{ + data := map[string]any{ "foo": "bar", } @@ -123,7 +123,7 @@ func TestRenderJsonpJSON(t *testing.T) { assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type")) w2 := httptest.NewRecorder() - datas := []map[string]interface{}{{ + datas := []map[string]any{{ "foo": "bar", }, { "bar": "foo", @@ -137,7 +137,7 @@ func TestRenderJsonpJSON(t *testing.T) { func TestRenderJsonpJSONError2(t *testing.T) { w := httptest.NewRecorder() - data := map[string]interface{}{ + data := map[string]any{ "foo": "bar", } (JsonpJSON{"", data}).WriteContentType(w) @@ -161,7 +161,7 @@ func TestRenderJsonpJSONFail(t *testing.T) { func TestRenderAsciiJSON(t *testing.T) { w1 := httptest.NewRecorder() - data1 := map[string]interface{}{ + data1 := map[string]any{ "lang": "GO语言", "tag": "
", } @@ -190,7 +190,7 @@ func TestRenderAsciiJSONFail(t *testing.T) { func TestRenderPureJSON(t *testing.T) { w := httptest.NewRecorder() - data := map[string]interface{}{ + data := map[string]any{ "foo": "bar", "html": "", } @@ -200,7 +200,7 @@ func TestRenderPureJSON(t *testing.T) { assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } -type xmlmap map[string]interface{} +type xmlmap map[string]any // Allows type H to be used with xml.Marshal func (h xmlmap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { @@ -244,7 +244,7 @@ b: type fail struct{} // Hook MarshalYAML -func (ft *fail) MarshalYAML() (interface{}, error) { +func (ft *fail) MarshalYAML() (any, error) { return nil, errors.New("fail") } @@ -358,13 +358,13 @@ func TestRenderString(t *testing.T) { (String{ Format: "hello %s %d", - Data: []interface{}{}, + Data: []any{}, }).WriteContentType(w) assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) err := (String{ Format: "hola %s %d", - Data: []interface{}{"manu", 2}, + Data: []any{"manu", 2}, }).Render(w) assert.NoError(t, err) @@ -377,7 +377,7 @@ func TestRenderStringLenZero(t *testing.T) { err := (String{ Format: "hola %s %d", - Data: []interface{}{}, + Data: []any{}, }).Render(w) assert.NoError(t, err) @@ -390,7 +390,7 @@ func TestRenderHTMLTemplate(t *testing.T) { templ := template.Must(template.New("t").Parse(`Hello {{.name}}`)) htmlRender := HTMLProduction{Template: templ} - instance := htmlRender.Instance("t", map[string]interface{}{ + instance := htmlRender.Instance("t", map[string]any{ "name": "alexandernyquist", }) @@ -406,7 +406,7 @@ func TestRenderHTMLTemplateEmptyName(t *testing.T) { templ := template.Must(template.New("").Parse(`Hello {{.name}}`)) htmlRender := HTMLProduction{Template: templ} - instance := htmlRender.Instance("", map[string]interface{}{ + instance := htmlRender.Instance("", map[string]any{ "name": "alexandernyquist", }) @@ -425,7 +425,7 @@ func TestRenderHTMLDebugFiles(t *testing.T) { Delims: Delims{Left: "{[{", Right: "}]}"}, FuncMap: nil, } - instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{ + instance := htmlRender.Instance("hello.tmpl", map[string]any{ "name": "thinkerou", }) @@ -444,7 +444,7 @@ func TestRenderHTMLDebugGlob(t *testing.T) { Delims: Delims{Left: "{[{", Right: "}]}"}, FuncMap: nil, } - instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{ + instance := htmlRender.Instance("hello.tmpl", map[string]any{ "name": "thinkerou", }) diff --git a/render/text.go b/render/text.go index 461b720af5..b77a776b0f 100644 --- a/render/text.go +++ b/render/text.go @@ -14,7 +14,7 @@ import ( // String contains the given interface object slice and its format. type String struct { Format string - Data []interface{} + Data []any } var plainContentType = []string{"text/plain; charset=utf-8"} @@ -30,7 +30,7 @@ func (r String) WriteContentType(w http.ResponseWriter) { } // WriteString writes data according to its format and write custom ContentType. -func WriteString(w http.ResponseWriter, format string, data []interface{}) (err error) { +func WriteString(w http.ResponseWriter, format string, data []any) (err error) { writeContentType(w, plainContentType) if len(data) > 0 { _, err = fmt.Fprintf(w, format, data...) diff --git a/render/xml.go b/render/xml.go index cc5390a2d0..c396a5a12e 100644 --- a/render/xml.go +++ b/render/xml.go @@ -11,7 +11,7 @@ import ( // XML contains the given interface object. type XML struct { - Data interface{} + Data any } var xmlContentType = []string{"application/xml; charset=utf-8"} diff --git a/render/yaml.go b/render/yaml.go index 0df7836089..0fc7a665d8 100644 --- a/render/yaml.go +++ b/render/yaml.go @@ -12,7 +12,7 @@ import ( // YAML contains the given interface object. type YAML struct { - Data interface{} + Data any } var yamlContentType = []string{"application/x-yaml; charset=utf-8"} diff --git a/testdata/protoexample/any.go b/testdata/protoexample/any.go new file mode 100644 index 0000000000..f16864c55e --- /dev/null +++ b/testdata/protoexample/any.go @@ -0,0 +1,10 @@ +// Copyright 2021 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build !go1.18 +// +build !go1.18 + +package protoexample + +type any = interface{} diff --git a/testdata/protoexample/test.pb.go b/testdata/protoexample/test.pb.go index bf45e02891..6687aae2be 100644 --- a/testdata/protoexample/test.pb.go +++ b/testdata/protoexample/test.pb.go @@ -231,7 +231,7 @@ func file_test_proto_rawDescGZIP() []byte { var file_test_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_test_proto_goTypes = []interface{}{ +var file_test_proto_goTypes = []any{ (FOO)(0), // 0: protoexample.FOO (*Test)(nil), // 1: protoexample.Test (*Test_OptionalGroup)(nil), // 2: protoexample.Test.OptionalGroup @@ -251,7 +251,7 @@ func file_test_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_test_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Test); i { case 0: return &v.state @@ -263,7 +263,7 @@ func file_test_proto_init() { return nil } } - file_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_test_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*Test_OptionalGroup); i { case 0: return &v.state diff --git a/tree_test.go b/tree_test.go index 94c533864e..085b58037b 100644 --- a/tree_test.go +++ b/tree_test.go @@ -357,7 +357,7 @@ func TestUnescapeParameters(t *testing.T) { checkPriorities(t, tree) } -func catchPanic(testFunc func()) (recv interface{}) { +func catchPanic(testFunc func()) (recv any) { defer func() { recv = recover() }() diff --git a/utils.go b/utils.go index e4599ea927..b41a3b0f6a 100644 --- a/utils.go +++ b/utils.go @@ -19,7 +19,7 @@ import ( const BindKey = "_gin-gonic/gin/bindkey" // Bind is a helper function for given interface object and returns a Gin middleware. -func Bind(val interface{}) HandlerFunc { +func Bind(val any) HandlerFunc { value := reflect.ValueOf(val) if value.Kind() == reflect.Ptr { panic(`Bind struct can not be a pointer. Example: @@ -51,7 +51,7 @@ func WrapH(h http.Handler) HandlerFunc { } // H is a shortcut for map[string]interface{} -type H map[string]interface{} +type H map[string]any // MarshalXML allows type H to be used with xml.Marshal. func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error { @@ -90,7 +90,7 @@ func filterFlags(content string) string { return content } -func chooseData(custom, wildcard interface{}) interface{} { +func chooseData(custom, wildcard any) any { if custom != nil { return custom } @@ -121,7 +121,7 @@ func lastChar(str string) uint8 { return str[len(str)-1] } -func nameOfFunction(f interface{}) string { +func nameOfFunction(f any) string { return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() } From 62265c893c5bd4bb1ba295008809fcc5dbd1d28d Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 21 Mar 2022 10:51:17 +0800 Subject: [PATCH 104/346] chore: support min version of go: 1.14 (#2964) --- .github/workflows/gin.yml | 2 +- README.md | 2 +- debug.go | 4 ++-- debug_test.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 1aecda580b..fa9d68b34b 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -28,7 +28,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: [1.13, 1.14, 1.15, 1.16, 1.17] + go: [1.14, 1.15, 1.16, 1.17] test-tags: ['', nomsgpack] include: - os: ubuntu-latest diff --git a/README.md b/README.md index 9a9785e642..4aa638d62b 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi To install Gin package, you need to install Go and set your Go workspace first. -1. The first need [Go](https://golang.org/) installed (**version 1.13+ is required**), then you can use the below Go command to install Gin. +1. The first need [Go](https://golang.org/) installed (**version 1.14+ is required**), then you can use the below Go command to install Gin. ```sh $ go get -u github.com/gin-gonic/gin diff --git a/debug.go b/debug.go index badcb5e1a3..d8d6c8e12c 100644 --- a/debug.go +++ b/debug.go @@ -12,7 +12,7 @@ import ( "strings" ) -const ginSupportMinGoVer = 13 +const ginSupportMinGoVer = 14 // IsDebugging returns true if the framework is running in debug mode. // Use SetMode(gin.ReleaseMode) to disable debug mode. @@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) { func debugPrintWARNINGDefault() { if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer { - debugPrint(`[WARNING] Now Gin requires Go 1.13+. + debugPrint(`[WARNING] Now Gin requires Go 1.14+. `) } diff --git a/debug_test.go b/debug_test.go index 0550999288..7c54444ae0 100644 --- a/debug_test.go +++ b/debug_test.go @@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { }) m, e := getMinVer(runtime.Version()) if e == nil && m <= ginSupportMinGoVer { - assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.13+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) + assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.14+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } else { assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } From be0d86edf49cd5984d727961cda85e8557835524 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 21 Mar 2022 17:38:11 +0800 Subject: [PATCH 105/346] chore(CI/CD): add go1.18 version (#3092) * chore(CI/CD): add go1.18 version Signed-off-by: Bo-Yi Wu * Update go.mod * Update go.sum * Update go.mod * Update go.sum Co-authored-by: thinkerou --- .github/workflows/gin.yml | 6 +++--- go.mod | 16 +++++++++++++++- go.sum | 1 - 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index fa9d68b34b..c432a33758 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -21,14 +21,14 @@ jobs: - name: Setup golangci-lint uses: golangci/golangci-lint-action@v3.1.0 with: - version: v1.44.0 + version: v1.45.0 args: --verbose test: needs: lint strategy: matrix: os: [ubuntu-latest, macos-latest] - go: [1.14, 1.15, 1.16, 1.17] + go: [1.14, 1.15, 1.16, 1.17, 1.18] test-tags: ['', nomsgpack] include: - os: ubuntu-latest @@ -51,7 +51,7 @@ jobs: uses: actions/checkout@v3 with: ref: ${{ github.ref }} - + - uses: actions/cache@v2 with: path: | diff --git a/go.mod b/go.mod index 7a9249c6e0..dcd83686d7 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gin-gonic/gin -go 1.13 +go 1.18 require ( github.com/gin-contrib/sse v0.1.0 @@ -14,3 +14,17 @@ require ( google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 ) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect + golang.org/x/text v0.3.6 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect +) diff --git a/go.sum b/go.sum index 6006ffb989..c0980ad527 100644 --- a/go.sum +++ b/go.sum @@ -47,7 +47,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= From 865fd560fc2f549cabb73452425dec22738c7b2d Mon Sep 17 00:00:00 2001 From: mstmdev Date: Wed, 23 Mar 2022 21:35:09 +0800 Subject: [PATCH 106/346] Update some comments, add function name prefix to comment (#3090) --- gin.go | 27 ++++++++++++++------------- logger.go | 6 +++--- response_writer.go | 16 ++++++++-------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/gin.go b/gin.go index b0e0154fac..c2e95f29b9 100644 --- a/gin.go +++ b/gin.go @@ -67,10 +67,10 @@ type RoutesInfo []RouteInfo // Trusted platforms const ( - // When running on Google App Engine. Trust X-Appengine-Remote-Addr + // PlatformGoogleAppEngine when running on Google App Engine. Trust X-Appengine-Remote-Addr // for determining the client's IP PlatformGoogleAppEngine = "X-Appengine-Remote-Addr" - // When using Cloudflare's CDN. Trust CF-Connecting-IP for determining + // PlatformCloudflare when using Cloudflare's CDN. Trust CF-Connecting-IP for determining // the client's IP PlatformCloudflare = "CF-Connecting-IP" ) @@ -80,14 +80,14 @@ const ( type Engine struct { RouterGroup - // Enables automatic redirection if the current route can't be matched but a + // RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a // handler for the path with (without) the trailing slash exists. // For example if /foo/ is requested but a route only exists for /foo, the // client is redirected to /foo with http status code 301 for GET requests // and 307 for all other request methods. RedirectTrailingSlash bool - // If enabled, the router tries to fix the current request path, if no + // RedirectFixedPath if enabled, the router tries to fix the current request path, if no // handle is registered for it. // First superfluous path elements like ../ or // are removed. // Afterwards the router does a case-insensitive lookup of the cleaned path. @@ -98,7 +98,7 @@ type Engine struct { // RedirectTrailingSlash is independent of this option. RedirectFixedPath bool - // If enabled, the router checks if another method is allowed for the + // HandleMethodNotAllowed if enabled, the router checks if another method is allowed for the // current route, if the current request can not be routed. // If this is the case, the request is answered with 'Method Not Allowed' // and HTTP status code 405. @@ -106,21 +106,22 @@ type Engine struct { // handler. HandleMethodNotAllowed bool - // If enabled, client IP will be parsed from the request's headers that + // ForwardedByClientIP if enabled, client IP will be parsed from the request's headers that // match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was // fetched, it falls back to the IP obtained from // `(*gin.Context).Request.RemoteAddr`. ForwardedByClientIP bool - // DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD + // AppEngine was deprecated. + // 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 - // If enabled, the url.RawPath will be used to find parameters. + // UseRawPath if enabled, the url.RawPath will be used to find parameters. UseRawPath bool - // If true, the path value will be unescaped. + // UnescapePathValues if true, the path value will be unescaped. // If UseRawPath is false (by default), the UnescapePathValues effectively is true, // as url.Path gonna be used, which is already unescaped. UnescapePathValues bool @@ -129,21 +130,21 @@ type Engine struct { // See the PR #1817 and issue #1644 RemoveExtraSlash bool - // List of headers used to obtain the client IP when + // RemoteIPHeaders list of headers used to obtain the client IP when // `(*gin.Engine).ForwardedByClientIP` is `true` and // `(*gin.Context).Request.RemoteAddr` is matched by at least one of the // network origins of list defined by `(*gin.Engine).SetTrustedProxies()`. RemoteIPHeaders []string - // If set to a constant of value gin.Platform*, trusts the headers set by + // TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by // that platform, for example to determine the client IP TrustedPlatform string - // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm + // MaxMultipartMemory value of 'maxMemory' param that is given to http.Request's ParseMultipartForm // method call. MaxMultipartMemory int64 - // Enable h2c support. + // UseH2C enable h2c support. UseH2C bool delims render.Delims diff --git a/logger.go b/logger.go index ab43d1123b..1f9d63ae7b 100644 --- a/logger.go +++ b/logger.go @@ -44,7 +44,7 @@ type LoggerConfig struct { // Optional. Default value is gin.DefaultWriter. Output io.Writer - // SkipPaths is a url path array which logs are not written. + // SkipPaths is an url path array which logs are not written. // Optional. SkipPaths []string } @@ -161,12 +161,12 @@ func ForceConsoleColor() { consoleColorMode = forceColor } -// ErrorLogger returns a handlerfunc for any error type. +// ErrorLogger returns a HandlerFunc for any error type. func ErrorLogger() HandlerFunc { return ErrorLoggerT(ErrorTypeAny) } -// ErrorLoggerT returns a handlerfunc for a given error type. +// ErrorLoggerT returns a HandlerFunc for a given error type. func ErrorLoggerT(typ ErrorType) HandlerFunc { return func(c *Context) { c.Next() diff --git a/response_writer.go b/response_writer.go index 26826689a5..7f9095f015 100644 --- a/response_writer.go +++ b/response_writer.go @@ -23,23 +23,23 @@ type ResponseWriter interface { http.Flusher http.CloseNotifier - // Returns the HTTP response status code of the current request. + // Status returns the HTTP response status code of the current request. Status() int - // Returns the number of bytes already written into the response http body. + // Size returns the number of bytes already written into the response http body. // See Written() Size() int - // Writes the string into the response body. + // WriteString writes the string into the response body. WriteString(string) (int, error) - // Returns true if the response body was already written. + // Written returns true if the response body was already written. Written() bool - // Forces to write the http header (status code + headers). + // WriteHeaderNow forces to write the http header (status code + headers). WriteHeaderNow() - // get the http.Pusher for server push + // Pusher get the http.Pusher for server push Pusher() http.Pusher } @@ -107,12 +107,12 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { return w.ResponseWriter.(http.Hijacker).Hijack() } -// CloseNotify implements the http.CloseNotify interface. +// CloseNotify implements the http.CloseNotifier interface. func (w *responseWriter) CloseNotify() <-chan bool { return w.ResponseWriter.(http.CloseNotifier).CloseNotify() } -// Flush implements the http.Flush interface. +// Flush implements the http.Flusher interface. func (w *responseWriter) Flush() { w.WriteHeaderNow() w.ResponseWriter.(http.Flusher).Flush() From 205bb8151cb73c51f92d893426e7f82a0b6c1744 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 08:44:37 +0800 Subject: [PATCH 107/346] chore(deps): bump actions/cache from 2 to 3 (#3093) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 c432a33758..f67641a918 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -52,7 +52,7 @@ jobs: with: ref: ${{ github.ref }} - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ${{ matrix.go-build }} From 3d55efe41962a7f26620444105b98e6778050218 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 12:02:20 +0800 Subject: [PATCH 108/346] chore(deps): bump google.golang.org/protobuf from 1.27.1 to 1.28.0 (#3104) 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 dcd83686d7..846bd5df0a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 - google.golang.org/protobuf v1.27.1 + google.golang.org/protobuf v1.28.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index c0980ad527..c92ce01ab0 100644 --- a/go.sum +++ b/go.sum @@ -67,8 +67,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 6a1d279c283bd8b36877651a02aa86944138d30d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 14:03:30 +0800 Subject: [PATCH 109/346] chore(deps): bump github.com/goccy/go-json from 0.9.5 to 0.9.6 (#3105) Bumps [github.com/goccy/go-json](https://github.com/goccy/go-json) from 0.9.5 to 0.9.6. - [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.9.5...v0.9.6) --- updated-dependencies: - dependency-name: github.com/goccy/go-json dependency-type: direct:production update-type: version-update:semver-patch ... 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 846bd5df0a..6c46755d40 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.10.0 - github.com/goccy/go-json v0.9.5 + github.com/goccy/go-json v0.9.6 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 c92ce01ab0..5108a5998e 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.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/goccy/go-json v0.9.5 h1:ooSMW526ZjK+EaL5elrSyN2EzIfi/3V0m4+HJEDYLik= -github.com/goccy/go-json v0.9.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.6 h1:5/4CtRQdtsX0sal8fdVhTaiMN01Ri8BExZZ8iRmHQ6E= +github.com/goccy/go-json v0.9.6/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 c4580944ae8edba96809660ffbfdadc6caaefc2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 14:03:54 +0800 Subject: [PATCH 110/346] chore(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.1 (#3094) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.0 to 1.7.1. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 6c46755d40..bfb462b8ed 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/goccy/go-json v0.9.6 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 google.golang.org/protobuf v1.28.0 diff --git a/go.sum b/go.sum index 5108a5998e..983b50a4d6 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,9 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= From 888b14ab283f3623174e99b6ae7555060731cd89 Mon Sep 17 00:00:00 2001 From: "Jonathan (JC) Chen" Date: Fri, 15 Apr 2022 18:52:09 -0700 Subject: [PATCH 111/346] docs: Update README.md (#3108) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4aa638d62b..af9a1e5379 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi To install Gin package, you need to install Go and set your Go workspace first. -1. The first need [Go](https://golang.org/) installed (**version 1.14+ is required**), then you can use the below Go command to install Gin. +1. You first need [Go](https://golang.org/) installed (**version 1.14+ is required**), then you can use the below Go command to install Gin. ```sh $ go get -u github.com/gin-gonic/gin From 68542126982eb2275ea0447c58b38cb38cff8ddc Mon Sep 17 00:00:00 2001 From: ahuigo <1781999+ahuigo@users.noreply.github.com> Date: Sun, 17 Apr 2022 12:41:59 +0800 Subject: [PATCH 112/346] Fix: missing `sameSite` when do context.reset() (#3123) --- context.go | 1 + 1 file changed, 1 insertion(+) diff --git a/context.go b/context.go index faa481332a..0fe1333170 100644 --- a/context.go +++ b/context.go @@ -98,6 +98,7 @@ func (c *Context) reset() { c.Accepted = nil c.queryCache = nil c.formCache = nil + c.sameSite = 0 *c.params = (*c.params)[:0] *c.skippedNodes = (*c.skippedNodes)[:0] } From 493b12482b913115d2664043f7aac0cafa96690a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 17 Apr 2022 14:15:53 +0800 Subject: [PATCH 113/346] chore(deps): bump actions/setup-go from 2 to 3 (#3118) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gin.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index f67641a918..771c0b9bce 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Setup go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.16' - name: Checkout repository @@ -43,7 +43,7 @@ jobs: GOPROXY: https://proxy.golang.org steps: - name: Set up Go ${{ matrix.go }} - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} From 696d37e0306fcb5e7399b61f6971024a431d0a90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 17 Apr 2022 14:16:17 +0800 Subject: [PATCH 114/346] chore(deps): bump codecov/codecov-action from 2 to 3 (#3117) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 771c0b9bce..c5e2744dd7 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -65,7 +65,7 @@ jobs: run: make test - name: Upload coverage to Codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }} notification-gitter: From 444e156fb1ec2943af5308a790315833c74fdc5a Mon Sep 17 00:00:00 2001 From: mstmdev Date: Thu, 21 Apr 2022 18:21:46 +0800 Subject: [PATCH 115/346] Fix some tests (#3100) * Sleep for one millisecond in the handler because the `Latency` will return `0s` sometimes and the test will fail * The `TCPListener.File` is not supported by windows, it is unimplemented now * Remove the `LF` in the `testdata/template/raw.tmpl`, because if set the git config `core.autocrlf=true`, will append `CR` to the raw.tmpl automatically, then test is failed on Windows --- gin_integration_test.go | 16 +++++++++++++++- gin_test.go | 4 ++-- logger_test.go | 1 + testdata/template/raw.tmpl | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/gin_integration_test.go b/gin_integration_test.go index 8c22e7bdad..0dfa903277 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -15,6 +15,7 @@ import ( "net/http/httptest" "os" "path/filepath" + "runtime" "sync" "testing" "time" @@ -281,7 +282,16 @@ func TestFileDescriptor(t *testing.T) { listener, err := net.ListenTCP("tcp", addr) assert.NoError(t, err) socketFile, err := listener.File() - assert.NoError(t, err) + if isWindows() { + // not supported by windows, it is unimplemented now + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + + if socketFile == nil { + return + } go func() { router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) @@ -547,3 +557,7 @@ func TestTreeRunDynamicRouting(t *testing.T) { testRequest(t, ts.URL+"/addr/dd/aa", "404 Not Found") testRequest(t, ts.URL+"/something/secondthing/121", "404 Not Found") } + +func isWindows() bool { + return runtime.GOOS == "windows" +} diff --git a/gin_test.go b/gin_test.go index 0c11134f04..a438062279 100644 --- a/gin_test.go +++ b/gin_test.go @@ -202,7 +202,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) { } resp, _ := ioutil.ReadAll(res.Body) - assert.Equal(t, "Date: 2017/07/01\n", string(resp)) + assert.Equal(t, "Date: 2017/07/01", string(resp)) } func init() { @@ -320,7 +320,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) { } resp, _ := ioutil.ReadAll(res.Body) - assert.Equal(t, "Date: 2017/07/01\n", string(resp)) + assert.Equal(t, "Date: 2017/07/01", string(resp)) } func TestAddRoute(t *testing.T) { diff --git a/logger_test.go b/logger_test.go index da1b654ec1..b704998891 100644 --- a/logger_test.go +++ b/logger_test.go @@ -208,6 +208,7 @@ func TestLoggerWithConfigFormatting(t *testing.T) { // set dummy ClientIP c.Request.Header.Set("X-Forwarded-For", "20.20.20.20") gotKeys = c.Keys + time.Sleep(time.Millisecond) }) PerformRequest(router, "GET", "/example?a=100") diff --git a/testdata/template/raw.tmpl b/testdata/template/raw.tmpl index 8bc757030d..f3f530a451 100644 --- a/testdata/template/raw.tmpl +++ b/testdata/template/raw.tmpl @@ -1 +1 @@ -Date: {[{.now | formatAsDate}]} +Date: {[{.now | formatAsDate}]} \ No newline at end of file From c706ace9298fe6440041ba0cb099ea4b3acda980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20B=C4=85k?= <56700396+53jk1@users.noreply.github.com> Date: Sat, 23 Apr 2022 12:01:03 +0200 Subject: [PATCH 116/346] fix: removed YODA conditions, removed blank identifier from `invalid_obj` (#3129) * fix: removed YODA conditions, unnecessary binding.binding * fix: remove BindingBody change --- binding/binding_test.go | 4 ++-- githubapi_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/binding/binding_test.go b/binding/binding_test.go index b1edbf5a03..d9746e266a 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -1339,10 +1339,10 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body err := b.Bind(req, &obj) assert.Error(t, err) - invalid_obj := FooStruct{} + invalidobj := FooStruct{} req.Body = ioutil.NopCloser(strings.NewReader(`{"msg":"hello"}`)) req.Header.Add("Content-Type", MIMEPROTOBUF) - err = b.Bind(req, &invalid_obj) + err = b.Bind(req, &invalidobj) assert.Error(t, err) assert.Equal(t, err.Error(), "obj is not ProtoMessage") diff --git a/githubapi_test.go b/githubapi_test.go index e74bddd55c..5fe65a4b1c 100644 --- a/githubapi_test.go +++ b/githubapi_test.go @@ -296,8 +296,8 @@ func TestShouldBindUri(t *testing.T) { router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { var person Person assert.NoError(t, c.ShouldBindUri(&person)) - assert.True(t, "" != person.Name) - assert.True(t, "" != person.ID) + assert.True(t, person.Name != "") + assert.True(t, person.ID != "") c.String(http.StatusOK, "ShouldBindUri test OK") }) @@ -318,8 +318,8 @@ func TestBindUri(t *testing.T) { router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { var person Person assert.NoError(t, c.BindUri(&person)) - assert.True(t, "" != person.Name) - assert.True(t, "" != person.ID) + assert.True(t, person.Name != "") + assert.True(t, person.ID != "") c.String(http.StatusOK, "BindUri test OK") }) From d8e053d15f2fa12f565c10cb92505d6e3e970da4 Mon Sep 17 00:00:00 2001 From: Lanco <35420416+lancoLiu@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:01:41 +0800 Subject: [PATCH 117/346] use StringToBytes func (#2798) --- auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth.go b/auth.go index 4d8a6ce484..482c499af5 100644 --- a/auth.go +++ b/auth.go @@ -31,7 +31,7 @@ func (a authPairs) searchCredential(authValue string) (string, bool) { return "", false } for _, pair := range a { - if subtle.ConstantTimeCompare([]byte(pair.value), []byte(authValue)) == 1 { + if subtle.ConstantTimeCompare(bytesconv.StringToBytes(pair.value), bytesconv.StringToBytes(authValue)) == 1 { return pair.user, true } } From e61cc06955d4d76a1781392577d0d1bcc1c60824 Mon Sep 17 00:00:00 2001 From: Faisal Alam Date: Sat, 23 Apr 2022 15:32:54 +0530 Subject: [PATCH 118/346] feat(context): return GIN Context from Value method (#2825) --- context.go | 6 ++++++ context_test.go | 1 + 2 files changed, 7 insertions(+) diff --git a/context.go b/context.go index 0fe1333170..5e53aaf0f2 100644 --- a/context.go +++ b/context.go @@ -39,6 +39,9 @@ const ( // BodyBytesKey indicates a default body bytes key. const BodyBytesKey = "_gin-gonic/gin/bodybyteskey" +// ContextKey is the key that a Context returns itself for. +const ContextKey = "_gin-gonic/gin/contextkey" + // abortIndex represents a typical value used in abort functions. const abortIndex int8 = math.MaxInt8 >> 1 @@ -1163,6 +1166,9 @@ func (c *Context) Value(key any) any { if key == 0 { return c.Request } + if key == ContextKey { + return c + } if keyAsString, ok := key.(string); ok { if val, exists := c.Get(keyAsString); exists { return val diff --git a/context_test.go b/context_test.go index fb46e6792f..20038e935f 100644 --- a/context_test.go +++ b/context_test.go @@ -1880,6 +1880,7 @@ func TestContextGolangContext(t *testing.T) { assert.Equal(t, ti, time.Time{}) assert.False(t, ok) assert.Equal(t, c.Value(0), c.Request) + assert.Equal(t, c.Value(ContextKey), c) assert.Nil(t, c.Value("foo")) c.Set("foo", "bar") From c131704fd6f31500f65f4b82073ef58dc4fc2fd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 08:50:28 +0800 Subject: [PATCH 119/346] chore(deps): bump github.com/goccy/go-json from 0.9.6 to 0.9.7 (#3131) 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 bfb462b8ed..2da1fe35c4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.10.0 - github.com/goccy/go-json v0.9.6 + github.com/goccy/go-json v0.9.7 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/stretchr/testify v1.7.1 diff --git a/go.sum b/go.sum index 983b50a4d6..d7a221f1c7 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.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/goccy/go-json v0.9.6 h1:5/4CtRQdtsX0sal8fdVhTaiMN01Ri8BExZZ8iRmHQ6E= -github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= +github.com/goccy/go-json v0.9.7/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 90e7073d56c0d2d8dc74f2325adc5c14527947db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 08:51:13 +0800 Subject: [PATCH 120/346] chore(deps): bump github/codeql-action from 1 to 2 (#3132) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4cbc4554fb..e27022d109 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -37,7 +37,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -46,4 +46,4 @@ jobs: # queries: ./path/to/local/query, your-org/your-repo/queries@main - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 From ef687e0db2947ddae312cb21bc2cdb8925611714 Mon Sep 17 00:00:00 2001 From: micanzhang Date: Sat, 14 May 2022 09:11:35 +0800 Subject: [PATCH 121/346] feat: automatically SetMode to TestMode when run go test. (#3139) related issue: https://github.com/gin-gonic/gin/issues/3134 --- mode.go | 7 ++++++- mode_test.go | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/mode.go b/mode.go index 1fb994b42c..4e7d734a72 100644 --- a/mode.go +++ b/mode.go @@ -5,6 +5,7 @@ package gin import ( + "flag" "io" "os" @@ -54,7 +55,11 @@ func init() { // SetMode sets gin mode according to input string. func SetMode(value string) { if value == "" { - value = DebugMode + if flag.Lookup("test.v") != nil { + value = TestMode + } else { + value = DebugMode + } } switch value { diff --git a/mode_test.go b/mode_test.go index 1b5fb2ff03..6fd9a13721 100644 --- a/mode_test.go +++ b/mode_test.go @@ -5,6 +5,7 @@ package gin import ( + "flag" "os" "testing" @@ -21,9 +22,16 @@ func TestSetMode(t *testing.T) { assert.Equal(t, TestMode, Mode()) os.Unsetenv(EnvGinMode) + SetMode("") + assert.Equal(t, testCode, ginMode) + assert.Equal(t, TestMode, Mode()) + + tmp := flag.CommandLine + flag.CommandLine = flag.NewFlagSet("", flag.ContinueOnError) SetMode("") assert.Equal(t, debugCode, ginMode) assert.Equal(t, DebugMode, Mode()) + flag.CommandLine = tmp SetMode(DebugMode) assert.Equal(t, debugCode, ginMode) From f1e942889abdab773538f215ec6aa242f994c6d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 May 2022 22:27:27 +0800 Subject: [PATCH 122/346] chore(deps): bump golangci/golangci-lint-action from 3.1.0 to 3.2.0 (#3150) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.1.0...v3.2.0) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action 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> --- .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 c5e2744dd7..e8ef30ca73 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -19,7 +19,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Setup golangci-lint - uses: golangci/golangci-lint-action@v3.1.0 + uses: golangci/golangci-lint-action@v3.2.0 with: version: v1.45.0 args: --verbose From 87811a97bddbe552ddd7f5ae2fa7bb949e787862 Mon Sep 17 00:00:00 2001 From: Eric_Lee Date: Sat, 28 May 2022 08:14:35 +0800 Subject: [PATCH 123/346] fix: the trusted proxies should support ipv6 address by default (#3033) --- gin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gin.go b/gin.go index c2e95f29b9..3a831e5b33 100644 --- a/gin.go +++ b/gin.go @@ -195,7 +195,7 @@ func New() *Engine { trees: make(methodTrees, 0, 9), delims: render.Delims{Left: "{{", Right: "}}"}, secureJSONPrefix: "while(1);", - trustedProxies: []string{"0.0.0.0/0"}, + trustedProxies: []string{"0.0.0.0/0", "::/0"}, trustedCIDRs: defaultTrustedCIDRs, } engine.RouterGroup.engine = engine From aa6002134e97efefd879b6819c1bfce114b05f42 Mon Sep 17 00:00:00 2001 From: Thibault Jamet Date: Sat, 28 May 2022 02:27:10 +0200 Subject: [PATCH 124/346] Fix intercepting headers in middlewares (#1271) * Fix intercepting headers in middlewares As explained in the TestInterceptedHeader test, in case a middleware filters out the headers, this middleware can be done inefficient in case one following handler is using c.String or other methods writing to the response body directly. This commit fixes the issue by using c.Writer when writing the Status as done in other c.Header, c.SetCookie and other response writers. The bug has been originally discovered using https://github.com/gin-contrib/gzip where a failing test has been added here: https://github.com/tjamet/gzip/blob/header/gzip_test.go#L71 Signed-off-by: Thibault Jamet * Skip Intercepted Header test for go <1.6 Signed-off-by: Thibault Jamet --- context_go17_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 context_go17_test.go diff --git a/context_go17_test.go b/context_go17_test.go new file mode 100644 index 0000000000..eca089ce09 --- /dev/null +++ b/context_go17_test.go @@ -0,0 +1,50 @@ +// +build go1.7 + +package gin + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +type interceptedWriter struct { + ResponseWriter + b *bytes.Buffer +} + +func (i interceptedWriter) WriteHeader(code int) { + i.Header().Del("X-Test") + i.ResponseWriter.WriteHeader(code) +} +func TestInterceptedHeader(t *testing.T) { + w := httptest.NewRecorder() + c, r := CreateTestContext(w) + + r.Use(func(c *Context) { + i := interceptedWriter{ + ResponseWriter: c.Writer, + b: bytes.NewBuffer(nil), + } + c.Writer = i + c.Next() + c.Header("X-Test", "overridden") + c.Writer = i.ResponseWriter + }) + r.GET("/", func(c *Context) { + c.Header("X-Test", "original") + c.Header("X-Test-2", "present") + c.String(http.StatusOK, "hello world") + }) + c.Request = httptest.NewRequest("GET", "/", nil) + r.HandleContext(c) + // Result() has headers frozen when WriteHeaderNow() has been called + // Compared to this time, this is when the response headers will be flushed + // As response is flushed on c.String, the Header cannot be set by the first + // middleware. Assert this + assert.Equal(t, "", w.Result().Header.Get("X-Test")) + assert.Equal(t, "present", w.Result().Header.Get("X-Test-2")) +} From ed03102ef0b08c552bc3d0a73fbd98307461abcc Mon Sep 17 00:00:00 2001 From: Valentine Oragbakosi Date: Fri, 27 May 2022 16:34:43 -0800 Subject: [PATCH 125/346] [GIN-001] - Add TOML bining for gin (#3081) Co-authored-by: GitstartHQ --- README.md | 6 +++--- binding/binding.go | 4 ++++ binding/binding_nomsgpack.go | 4 ++++ binding/binding_test.go | 17 +++++++++++++++++ binding/toml.go | 31 +++++++++++++++++++++++++++++++ binding/toml_test.go | 22 ++++++++++++++++++++++ context.go | 21 +++++++++++++++++++++ context_test.go | 17 +++++++++++++++++ go.mod | 1 + go.sum | 4 ++++ render/render.go | 1 + render/toml.go | 32 ++++++++++++++++++++++++++++++++ 12 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 binding/toml.go create mode 100644 binding/toml_test.go create mode 100644 render/toml.go diff --git a/README.md b/README.md index af9a1e5379..6b4cabb41b 100644 --- a/README.md +++ b/README.md @@ -658,7 +658,7 @@ func main() { ### Model binding and validation -To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz). +To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML, TOML and standard form values (foo=bar&boo=baz). Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags). @@ -666,10 +666,10 @@ Note that you need to set the corresponding binding tag on all fields you want t Also, Gin provides two sets of methods for binding: - **Type** - Must bind - - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader` + - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader`, `BindTOML` - **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method. - **Type** - Should bind - - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader` + - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader`, `ShouldBindTOML`, - **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately. When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`. diff --git a/binding/binding.go b/binding/binding.go index 703a1cf87e..50510514cd 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -22,6 +22,7 @@ const ( MIMEMSGPACK = "application/x-msgpack" MIMEMSGPACK2 = "application/msgpack" MIMEYAML = "application/x-yaml" + MIMETOML = "application/toml" ) // Binding describes the interface which needs to be implemented for binding the @@ -83,6 +84,7 @@ var ( YAML = yamlBinding{} Uri = uriBinding{} Header = headerBinding{} + TOML = tomlBinding{} ) // Default returns the appropriate Binding instance based on the HTTP method @@ -103,6 +105,8 @@ func Default(method, contentType string) Binding { return MsgPack case MIMEYAML: return YAML + case MIMETOML: + return TOML case MIMEMultipartPOSTForm: return FormMultipart default: // case MIMEPOSTForm: diff --git a/binding/binding_nomsgpack.go b/binding/binding_nomsgpack.go index b3818549f3..7f6a904ab0 100644 --- a/binding/binding_nomsgpack.go +++ b/binding/binding_nomsgpack.go @@ -20,6 +20,7 @@ const ( MIMEMultipartPOSTForm = "multipart/form-data" MIMEPROTOBUF = "application/x-protobuf" MIMEYAML = "application/x-yaml" + MIMETOML = "application/toml" ) // Binding describes the interface which needs to be implemented for binding the @@ -79,6 +80,7 @@ var ( YAML = yamlBinding{} Uri = uriBinding{} Header = headerBinding{} + TOML = tomlBinding{} ) // Default returns the appropriate Binding instance based on the HTTP method @@ -99,6 +101,8 @@ func Default(method, contentType string) Binding { return YAML case MIMEMultipartPOSTForm: return FormMultipart + case MIMETOML: + return TOML default: // case MIMEPOSTForm: return Form } diff --git a/binding/binding_test.go b/binding/binding_test.go index d9746e266a..f08e173f0a 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -165,6 +165,9 @@ func TestBindingDefault(t *testing.T) { assert.Equal(t, YAML, Default("POST", MIMEYAML)) assert.Equal(t, YAML, Default("PUT", MIMEYAML)) + + assert.Equal(t, TOML, Default("POST", MIMETOML)) + assert.Equal(t, TOML, Default("PUT", MIMETOML)) } func TestBindingJSONNilBody(t *testing.T) { @@ -454,6 +457,20 @@ func TestBindingXMLFail(t *testing.T) { "bar", "foo") } +func TestBindingTOML(t *testing.T) { + testBodyBinding(t, + TOML, "toml", + "/", "/", + `foo="bar"`, `bar="foo"`) +} + +func TestBindingTOMLFail(t *testing.T) { + testBodyBindingFail(t, + TOML, "toml", + "/", "/", + `foo=\n"bar"`, `bar="foo"`) +} + func TestBindingYAML(t *testing.T) { testBodyBinding(t, YAML, "yaml", diff --git a/binding/toml.go b/binding/toml.go new file mode 100644 index 0000000000..5b9ad165d1 --- /dev/null +++ b/binding/toml.go @@ -0,0 +1,31 @@ +package binding + +import ( + "bytes" + "io" + "net/http" + + "github.com/pelletier/go-toml/v2" +) + +type tomlBinding struct{} + +func (tomlBinding) Name() string { + return "toml" +} + +func decodeToml(r io.Reader, obj any) error { + decoder := toml.NewDecoder(r) + if err := decoder.Decode(obj); err != nil { + return err + } + return decoder.Decode(obj) +} + +func (tomlBinding) Bind(req *http.Request, obj any) error { + return decodeToml(req.Body, obj) +} + +func (tomlBinding) BindBody(body []byte, obj any) error { + return decodeToml(bytes.NewReader(body), obj) +} diff --git a/binding/toml_test.go b/binding/toml_test.go new file mode 100644 index 0000000000..2bc0e3a47e --- /dev/null +++ b/binding/toml_test.go @@ -0,0 +1,22 @@ +// Copyright 2022 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTOMLBindingBindBody(t *testing.T) { + var s struct { + Foo string `toml:"foo"` + } + tomlBody := `foo="FOO"` + err := tomlBinding{}.BindBody([]byte(tomlBody), &s) + require.NoError(t, err) + assert.Equal(t, "FOO", s.Foo) +} diff --git a/context.go b/context.go index 5e53aaf0f2..ecbd3dac7d 100644 --- a/context.go +++ b/context.go @@ -34,6 +34,7 @@ const ( MIMEPOSTForm = binding.MIMEPOSTForm MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm MIMEYAML = binding.MIMEYAML + MIMETOML = binding.MIMETOML ) // BodyBytesKey indicates a default body bytes key. @@ -636,6 +637,11 @@ func (c *Context) BindYAML(obj any) error { return c.MustBindWith(obj, binding.YAML) } +// BindTOML is a shortcut for c.MustBindWith(obj, binding.TOML). +func (c *Context) BindTOML(obj interface{}) error { + return c.MustBindWith(obj, binding.TOML) +} + // BindHeader is a shortcut for c.MustBindWith(obj, binding.Header). func (c *Context) BindHeader(obj any) error { return c.MustBindWith(obj, binding.Header) @@ -694,6 +700,11 @@ func (c *Context) ShouldBindYAML(obj any) error { return c.ShouldBindWith(obj, binding.YAML) } +// ShouldBindTOML is a shortcut for c.ShouldBindWith(obj, binding.TOML). +func (c *Context) ShouldBindTOML(obj interface{}) error { + return c.ShouldBindWith(obj, binding.TOML) +} + // ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header). func (c *Context) ShouldBindHeader(obj any) error { return c.ShouldBindWith(obj, binding.Header) @@ -965,6 +976,11 @@ func (c *Context) YAML(code int, obj any) { c.Render(code, render.YAML{Data: obj}) } +// TOML serializes the given struct as TOML into the response body. +func (c *Context) TOML(code int, obj interface{}) { + c.Render(code, render.TOML{Data: obj}) +} + // ProtoBuf serializes the given struct as ProtoBuf into the response body. func (c *Context) ProtoBuf(code int, obj any) { c.Render(code, render.ProtoBuf{Data: obj}) @@ -1069,6 +1085,7 @@ type Negotiate struct { XMLData any YAMLData any Data any + TOMLData any } // Negotiate calls different Render according to acceptable Accept format. @@ -1090,6 +1107,10 @@ func (c *Context) Negotiate(code int, config Negotiate) { data := chooseData(config.YAMLData, config.Data) c.YAML(code, data) + case binding.MIMETOML: + data := chooseData(config.TOMLData, config.Data) + c.TOML(code, data) + default: c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck } diff --git a/context_test.go b/context_test.go index 20038e935f..7e4d0b3fde 100644 --- a/context_test.go +++ b/context_test.go @@ -1773,6 +1773,23 @@ func TestContextShouldBindWithYAML(t *testing.T) { assert.Equal(t, 0, w.Body.Len()) } +func TestContextShouldBindWithTOML(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'")) + c.Request.Header.Add("Content-Type", MIMETOML) // set fake content-type + + var obj struct { + Foo string `toml:"foo"` + Bar string `toml:"bar"` + } + assert.NoError(t, c.ShouldBindTOML(&obj)) + assert.Equal(t, "foo", obj.Bar) + assert.Equal(t, "bar", obj.Foo) + assert.Equal(t, 0, w.Body.Len()) +} + func TestContextBadAutoShouldBind(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) diff --git a/go.mod b/go.mod index 2da1fe35c4..e519b36ec2 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/goccy/go-json v0.9.7 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 + github.com/pelletier/go-toml/v2 v2.0.0-beta.6 github.com/stretchr/testify v1.7.1 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 diff --git a/go.sum b/go.sum index d7a221f1c7..d7555f9a6c 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.0.0-beta.6 h1:JFNqj2afbbhCqTiyN16D7Tudc/aaDzE2FBDk+VlBQnE= +github.com/pelletier/go-toml/v2 v2.0.0-beta.6/go.mod h1:ke6xncR3W76Ba8xnVxkrZG0js6Rd2BsQEAYrfgJ6eQA= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -46,8 +48,10 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= diff --git a/render/render.go b/render/render.go index bcd568bfba..1fa48061fa 100644 --- a/render/render.go +++ b/render/render.go @@ -30,6 +30,7 @@ var ( _ Render = Reader{} _ Render = AsciiJSON{} _ Render = ProtoBuf{} + _ Render = TOML{} ) func writeContentType(w http.ResponseWriter, value []string) { diff --git a/render/toml.go b/render/toml.go new file mode 100644 index 0000000000..1192c78cbd --- /dev/null +++ b/render/toml.go @@ -0,0 +1,32 @@ +package render + +import ( + "net/http" + + "github.com/pelletier/go-toml/v2" +) + +// TOML contains the given interface object. +type TOML struct { + Data any +} + +var TOMLContentType = []string{"application/toml; charset=utf-8"} + +// Render (TOML) marshals the given interface object and writes data with custom ContentType. +func (r TOML) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + + bytes, err := toml.Marshal(r.Data) + if err != nil { + return err + } + + _, err = w.Write(bytes) + return err +} + +// WriteContentType (TOML) writes TOML ContentType for response. +func (r TOML) WriteContentType(w http.ResponseWriter) { + writeContentType(w, TOMLContentType) +} From 4b68a5f12af4d6d2be83e1895f783d5dd5d5a148 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sat, 28 May 2022 10:42:28 +0800 Subject: [PATCH 126/346] chore: update go.mod and remove space from copyright (#3158) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- any.go | 2 +- auth.go | 2 +- auth_test.go | 2 +- benchmarks_test.go | 2 +- binding/any.go | 2 +- binding/binding.go | 2 +- binding/binding_test.go | 2 +- binding/default_validator.go | 2 +- binding/default_validator_benchmark_test.go | 4 ++ binding/form.go | 2 +- binding/form_mapping.go | 2 +- binding/form_mapping_benchmark_test.go | 2 +- binding/header.go | 4 ++ binding/json.go | 2 +- binding/msgpack.go | 2 +- binding/multipart_form_mapping.go | 2 +- binding/multipart_form_mapping_test.go | 2 +- binding/protobuf.go | 2 +- binding/query.go | 2 +- binding/toml.go | 4 ++ binding/validate_test.go | 2 +- binding/xml.go | 2 +- binding/yaml.go | 2 +- context.go | 2 +- context_1.16_test.go | 2 +- context_1.17_test.go | 41 ++++++++++++++++- context_appengine.go | 2 +- context_go17_test.go | 50 --------------------- context_test.go | 2 +- debug.go | 2 +- debug_test.go | 2 +- deprecated.go | 2 +- deprecated_test.go | 2 +- errors.go | 2 +- errors_test.go | 2 +- fs.go | 2 +- gin.go | 2 +- ginS/gins.go | 2 +- gin_integration_test.go | 2 +- gin_test.go | 2 +- githubapi_test.go | 2 +- go.mod | 2 +- go.sum | 6 +-- internal/json/go_json.go | 2 +- internal/json/json.go | 2 +- internal/json/jsoniter.go | 2 +- logger.go | 2 +- logger_test.go | 2 +- middleware_test.go | 2 +- mode.go | 2 +- mode_test.go | 2 +- recovery.go | 2 +- recovery_test.go | 2 +- render/any.go | 2 +- render/data.go | 2 +- render/html.go | 2 +- render/json.go | 2 +- render/msgpack.go | 2 +- render/protobuf.go | 2 +- render/reader.go | 2 +- render/reader_test.go | 2 +- render/redirect.go | 2 +- render/render.go | 2 +- render/render_test.go | 2 +- render/text.go | 2 +- render/toml.go | 4 ++ render/xml.go | 2 +- render/yaml.go | 2 +- response_writer.go | 2 +- response_writer_test.go | 2 +- routergroup.go | 2 +- routergroup_test.go | 2 +- routes_test.go | 2 +- test_helpers.go | 2 +- testdata/protoexample/any.go | 2 +- utils.go | 2 +- utils_test.go | 2 +- version.go | 2 +- 78 files changed, 129 insertions(+), 126 deletions(-) delete mode 100644 context_go17_test.go diff --git a/any.go b/any.go index a0104dc899..42b1ea46f7 100644 --- a/any.go +++ b/any.go @@ -1,4 +1,4 @@ -// Copyright 2022 Gin Core Team. All rights reserved. +// Copyright 2022 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/auth.go b/auth.go index 482c499af5..2503c5156a 100644 --- a/auth.go +++ b/auth.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/auth_test.go b/auth_test.go index e44bd100ac..42b6f8fddf 100644 --- a/auth_test.go +++ b/auth_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/benchmarks_test.go b/benchmarks_test.go index 0b3f82df7d..5b7929b854 100644 --- a/benchmarks_test.go +++ b/benchmarks_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/any.go b/binding/any.go index 1331d390dd..d8251a7c20 100644 --- a/binding/any.go +++ b/binding/any.go @@ -1,4 +1,4 @@ -// Copyright 2022 Gin Core Team. All rights reserved. +// Copyright 2022 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/binding.go b/binding/binding.go index 50510514cd..a58924ed3e 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/binding_test.go b/binding/binding_test.go index f08e173f0a..f09962164d 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/default_validator.go b/binding/default_validator.go index 3515a8cc57..c03afe75be 100644 --- a/binding/default_validator.go +++ b/binding/default_validator.go @@ -1,4 +1,4 @@ -// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/default_validator_benchmark_test.go b/binding/default_validator_benchmark_test.go index 8d628369ea..9292e2aaf1 100644 --- a/binding/default_validator_benchmark_test.go +++ b/binding/default_validator_benchmark_test.go @@ -1,3 +1,7 @@ +// Copyright 2022 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + package binding import ( diff --git a/binding/form.go b/binding/form.go index f5cbf57b2e..b17352bad0 100644 --- a/binding/form.go +++ b/binding/form.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/form_mapping.go b/binding/form_mapping.go index c24dd553df..98cebfecf7 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/form_mapping_benchmark_test.go b/binding/form_mapping_benchmark_test.go index 9572ea03dc..5788133f2d 100644 --- a/binding/form_mapping_benchmark_test.go +++ b/binding/form_mapping_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Gin Core Team. All rights reserved. +// Copyright 2019 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/header.go b/binding/header.go index 14f525ef5f..03bc78dae1 100644 --- a/binding/header.go +++ b/binding/header.go @@ -1,3 +1,7 @@ +// Copyright 2022 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + package binding import ( diff --git a/binding/json.go b/binding/json.go index 2e3e1dd5c3..36eb27a344 100644 --- a/binding/json.go +++ b/binding/json.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/msgpack.go b/binding/msgpack.go index 65197713e6..d1f035e44a 100644 --- a/binding/msgpack.go +++ b/binding/msgpack.go @@ -1,4 +1,4 @@ -// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/multipart_form_mapping.go b/binding/multipart_form_mapping.go index c4d7ed7421..4ebe83263c 100644 --- a/binding/multipart_form_mapping.go +++ b/binding/multipart_form_mapping.go @@ -1,4 +1,4 @@ -// Copyright 2019 Gin Core Team. All rights reserved. +// Copyright 2019 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go index 545914d054..9932860386 100644 --- a/binding/multipart_form_mapping_test.go +++ b/binding/multipart_form_mapping_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Gin Core Team. All rights reserved. +// Copyright 2019 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/protobuf.go b/binding/protobuf.go index ace8f6547d..44f2fdb93d 100644 --- a/binding/protobuf.go +++ b/binding/protobuf.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/query.go b/binding/query.go index 9790ce6a26..c958b88bda 100644 --- a/binding/query.go +++ b/binding/query.go @@ -1,4 +1,4 @@ -// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/toml.go b/binding/toml.go index 5b9ad165d1..83b27267b1 100644 --- a/binding/toml.go +++ b/binding/toml.go @@ -1,3 +1,7 @@ +// Copyright 2022 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + package binding import ( diff --git a/binding/validate_test.go b/binding/validate_test.go index d05c891ea1..801bd9b7c5 100644 --- a/binding/validate_test.go +++ b/binding/validate_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/xml.go b/binding/xml.go index e62af4a679..a70f4ad36c 100644 --- a/binding/xml.go +++ b/binding/xml.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/yaml.go b/binding/yaml.go index 183f141ed6..b0d36a3589 100644 --- a/binding/yaml.go +++ b/binding/yaml.go @@ -1,4 +1,4 @@ -// Copyright 2018 Gin Core Team. All rights reserved. +// Copyright 2018 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/context.go b/context.go index ecbd3dac7d..6b25b3a31d 100644 --- a/context.go +++ b/context.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/context_1.16_test.go b/context_1.16_test.go index 053e6c5aab..267605072e 100644 --- a/context_1.16_test.go +++ b/context_1.16_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 Gin Core Team. All rights reserved. +// Copyright 2021 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/context_1.17_test.go b/context_1.17_test.go index 431d54c7bf..69c97864ce 100644 --- a/context_1.17_test.go +++ b/context_1.17_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 Gin Core Team. All rights reserved. +// Copyright 2021 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. @@ -17,6 +17,16 @@ import ( "github.com/stretchr/testify/assert" ) +type interceptedWriter struct { + ResponseWriter + b *bytes.Buffer +} + +func (i interceptedWriter) WriteHeader(code int) { + i.Header().Del("X-Test") + i.ResponseWriter.WriteHeader(code) +} + func TestContextFormFileFailed17(t *testing.T) { buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) @@ -31,3 +41,32 @@ func TestContextFormFileFailed17(t *testing.T) { assert.Nil(t, f) }) } + +func TestInterceptedHeader(t *testing.T) { + w := httptest.NewRecorder() + c, r := CreateTestContext(w) + + r.Use(func(c *Context) { + i := interceptedWriter{ + ResponseWriter: c.Writer, + b: bytes.NewBuffer(nil), + } + c.Writer = i + c.Next() + c.Header("X-Test", "overridden") + c.Writer = i.ResponseWriter + }) + r.GET("/", func(c *Context) { + c.Header("X-Test", "original") + c.Header("X-Test-2", "present") + c.String(http.StatusOK, "hello world") + }) + c.Request = httptest.NewRequest("GET", "/", nil) + r.HandleContext(c) + // Result() has headers frozen when WriteHeaderNow() has been called + // Compared to this time, this is when the response headers will be flushed + // As response is flushed on c.String, the Header cannot be set by the first + // middleware. Assert this + assert.Equal(t, "", w.Result().Header.Get("X-Test")) + assert.Equal(t, "present", w.Result().Header.Get("X-Test-2")) +} diff --git a/context_appengine.go b/context_appengine.go index 8bf938961d..931313f613 100644 --- a/context_appengine.go +++ b/context_appengine.go @@ -1,4 +1,4 @@ -// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/context_go17_test.go b/context_go17_test.go deleted file mode 100644 index eca089ce09..0000000000 --- a/context_go17_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// +build go1.7 - -package gin - -import ( - "bytes" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" -) - -type interceptedWriter struct { - ResponseWriter - b *bytes.Buffer -} - -func (i interceptedWriter) WriteHeader(code int) { - i.Header().Del("X-Test") - i.ResponseWriter.WriteHeader(code) -} -func TestInterceptedHeader(t *testing.T) { - w := httptest.NewRecorder() - c, r := CreateTestContext(w) - - r.Use(func(c *Context) { - i := interceptedWriter{ - ResponseWriter: c.Writer, - b: bytes.NewBuffer(nil), - } - c.Writer = i - c.Next() - c.Header("X-Test", "overridden") - c.Writer = i.ResponseWriter - }) - r.GET("/", func(c *Context) { - c.Header("X-Test", "original") - c.Header("X-Test-2", "present") - c.String(http.StatusOK, "hello world") - }) - c.Request = httptest.NewRequest("GET", "/", nil) - r.HandleContext(c) - // Result() has headers frozen when WriteHeaderNow() has been called - // Compared to this time, this is when the response headers will be flushed - // As response is flushed on c.String, the Header cannot be set by the first - // middleware. Assert this - assert.Equal(t, "", w.Result().Header.Get("X-Test")) - assert.Equal(t, "present", w.Result().Header.Get("X-Test-2")) -} diff --git a/context_test.go b/context_test.go index 7e4d0b3fde..f9f0c1dc19 100644 --- a/context_test.go +++ b/context_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/debug.go b/debug.go index d8d6c8e12c..25fd7c87f9 100644 --- a/debug.go +++ b/debug.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/debug_test.go b/debug_test.go index 7c54444ae0..4ac55fe148 100644 --- a/debug_test.go +++ b/debug_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/deprecated.go b/deprecated.go index 004e13dbb6..fdad855477 100644 --- a/deprecated.go +++ b/deprecated.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/deprecated_test.go b/deprecated_test.go index f8df651c34..0240b2ec75 100644 --- a/deprecated_test.go +++ b/deprecated_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/errors.go b/errors.go index 578e857d6a..2853ce8ed8 100644 --- a/errors.go +++ b/errors.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/errors_test.go b/errors_test.go index ac72dc42c9..78d561c63b 100644 --- a/errors_test.go +++ b/errors_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/fs.go b/fs.go index e5f3d602a3..64274735e2 100644 --- a/fs.go +++ b/fs.go @@ -1,4 +1,4 @@ -// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/gin.go b/gin.go index 3a831e5b33..e516360caa 100644 --- a/gin.go +++ b/gin.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/ginS/gins.go b/ginS/gins.go index 0802e08567..1550b868d1 100644 --- a/ginS/gins.go +++ b/ginS/gins.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/gin_integration_test.go b/gin_integration_test.go index 0dfa903277..b0532a25d1 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/gin_test.go b/gin_test.go index a438062279..02f23247d5 100644 --- a/gin_test.go +++ b/gin_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/githubapi_test.go b/githubapi_test.go index 5fe65a4b1c..c6350e813e 100644 --- a/githubapi_test.go +++ b/githubapi_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/go.mod b/go.mod index e519b36ec2..6cdde34e63 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/goccy/go-json v0.9.7 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 - github.com/pelletier/go-toml/v2 v2.0.0-beta.6 + github.com/pelletier/go-toml/v2 v2.0.1 github.com/stretchr/testify v1.7.1 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 diff --git a/go.sum b/go.sum index d7555f9a6c..d06b810376 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.0-beta.6 h1:JFNqj2afbbhCqTiyN16D7Tudc/aaDzE2FBDk+VlBQnE= -github.com/pelletier/go-toml/v2 v2.0.0-beta.6/go.mod h1:ke6xncR3W76Ba8xnVxkrZG0js6Rd2BsQEAYrfgJ6eQA= +github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -48,10 +48,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= diff --git a/internal/json/go_json.go b/internal/json/go_json.go index da960571a8..23f717265e 100644 --- a/internal/json/go_json.go +++ b/internal/json/go_json.go @@ -1,4 +1,4 @@ -// Copyright 2017 Bo-Yi Wu. All rights reserved. +// Copyright 2017 Bo-Yi Wu. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/internal/json/json.go b/internal/json/json.go index 75b6022409..a26d7db2e2 100644 --- a/internal/json/json.go +++ b/internal/json/json.go @@ -1,4 +1,4 @@ -// Copyright 2017 Bo-Yi Wu. All rights reserved. +// Copyright 2017 Bo-Yi Wu. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/internal/json/jsoniter.go b/internal/json/jsoniter.go index 232f8dcada..853b1a901e 100644 --- a/internal/json/jsoniter.go +++ b/internal/json/jsoniter.go @@ -1,4 +1,4 @@ -// Copyright 2017 Bo-Yi Wu. All rights reserved. +// Copyright 2017 Bo-Yi Wu. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/logger.go b/logger.go index 1f9d63ae7b..cd1e7fa6e9 100644 --- a/logger.go +++ b/logger.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/logger_test.go b/logger_test.go index b704998891..fa0d9ce8be 100644 --- a/logger_test.go +++ b/logger_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/middleware_test.go b/middleware_test.go index e0a756c3de..a235fe9192 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/mode.go b/mode.go index 4e7d734a72..545fdaaf8f 100644 --- a/mode.go +++ b/mode.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/mode_test.go b/mode_test.go index 6fd9a13721..2407f46339 100644 --- a/mode_test.go +++ b/mode_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/recovery.go b/recovery.go index 3efe146b14..abb645105a 100644 --- a/recovery.go +++ b/recovery.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/recovery_test.go b/recovery_test.go index 2c327a6586..347917e7b5 100644 --- a/recovery_test.go +++ b/recovery_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/any.go b/render/any.go index 57349be316..b19ad45d9f 100644 --- a/render/any.go +++ b/render/any.go @@ -1,4 +1,4 @@ -// Copyright 2021 Gin Core Team. All rights reserved. +// Copyright 2021 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/data.go b/render/data.go index 6ba657ba0a..a653ea3091 100644 --- a/render/data.go +++ b/render/data.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/html.go b/render/html.go index bdfaf11ace..c308408d2a 100644 --- a/render/html.go +++ b/render/html.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/json.go b/render/json.go index 0a7dcef290..af678e80d1 100644 --- a/render/json.go +++ b/render/json.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/msgpack.go b/render/msgpack.go index 2c0e9aa8d0..e0f30f7a97 100644 --- a/render/msgpack.go +++ b/render/msgpack.go @@ -1,4 +1,4 @@ -// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/protobuf.go b/render/protobuf.go index 2e57f5ca0f..9331c40583 100644 --- a/render/protobuf.go +++ b/render/protobuf.go @@ -1,4 +1,4 @@ -// Copyright 2018 Gin Core Team. All rights reserved. +// Copyright 2018 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/reader.go b/render/reader.go index d5282e4927..5752d8d859 100644 --- a/render/reader.go +++ b/render/reader.go @@ -1,4 +1,4 @@ -// Copyright 2018 Gin Core Team. All rights reserved. +// Copyright 2018 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/reader_test.go b/render/reader_test.go index 3930f51df1..aaceb9eaac 100644 --- a/render/reader_test.go +++ b/render/reader_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Gin Core Team. All rights reserved. +// Copyright 2019 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/redirect.go b/render/redirect.go index c006691ca6..70e3a47e81 100644 --- a/render/redirect.go +++ b/render/redirect.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/render.go b/render/render.go index 1fa48061fa..7955000c6e 100644 --- a/render/render.go +++ b/render/render.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/render_test.go b/render/render_test.go index 8b28dc3f0e..a13fff4242 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/text.go b/render/text.go index b77a776b0f..77eafdfd84 100644 --- a/render/text.go +++ b/render/text.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/toml.go b/render/toml.go index 1192c78cbd..40f044c88a 100644 --- a/render/toml.go +++ b/render/toml.go @@ -1,3 +1,7 @@ +// Copyright 2022 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + package render import ( diff --git a/render/xml.go b/render/xml.go index c396a5a12e..6af8901796 100644 --- a/render/xml.go +++ b/render/xml.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/yaml.go b/render/yaml.go index 0fc7a665d8..4f0ac01f62 100644 --- a/render/yaml.go +++ b/render/yaml.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/response_writer.go b/response_writer.go index 7f9095f015..77c7ed8fda 100644 --- a/response_writer.go +++ b/response_writer.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/response_writer_test.go b/response_writer_test.go index 9061d02101..57d163c9f1 100644 --- a/response_writer_test.go +++ b/response_writer_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/routergroup.go b/routergroup.go index 3fba3a9107..3c082d932b 100644 --- a/routergroup.go +++ b/routergroup.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/routergroup_test.go b/routergroup_test.go index c1fad3a984..41f963724e 100644 --- a/routergroup_test.go +++ b/routergroup_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/routes_test.go b/routes_test.go index 5643097069..d7034b2292 100644 --- a/routes_test.go +++ b/routes_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/test_helpers.go b/test_helpers.go index 3a7a5ddf69..b3be93b4ee 100644 --- a/test_helpers.go +++ b/test_helpers.go @@ -1,4 +1,4 @@ -// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/testdata/protoexample/any.go b/testdata/protoexample/any.go index f16864c55e..2203f33a32 100644 --- a/testdata/protoexample/any.go +++ b/testdata/protoexample/any.go @@ -1,4 +1,4 @@ -// Copyright 2021 Gin Core Team. All rights reserved. +// Copyright 2021 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/utils.go b/utils.go index b41a3b0f6a..4021a2ab40 100644 --- a/utils.go +++ b/utils.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/utils_test.go b/utils_test.go index d2a740bfb1..058ddb9d70 100644 --- a/utils_test.go +++ b/utils_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/version.go b/version.go index 4b69b9b91c..40e6505b31 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ -// Copyright 2018 Gin Core Team. All rights reserved. +// Copyright 2018 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. From 60e24d5690296e583c1ad67579b1f016da76df3d Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 28 May 2022 15:23:00 +0800 Subject: [PATCH 127/346] chore(CI/CD): add go version release flow (#3159) * chore(CI/CD): add go version release flow Signed-off-by: Bo-Yi Wu * chore: bump to v1.8.0 Signed-off-by: Bo-Yi Wu * chore: update Signed-off-by: Bo-Yi Wu --- .github/workflows/goreleaser.yml | 34 +++++++++++++++++++ .goreleaser.yaml | 57 ++++++++++++++++++++++++++++++++ version.go | 2 +- 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/goreleaser.yml create mode 100644 .goreleaser.yaml diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml new file mode 100644 index 0000000000..e0f6e4f6fb --- /dev/null +++ b/.github/workflows/goreleaser.yml @@ -0,0 +1,34 @@ +name: Goreleaser + +on: + push: + tags: + - '*' + +permissions: + contents: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - + name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.17 + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + # either 'goreleaser' (default) or 'goreleaser-pro' + distribution: goreleaser + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000000..e435e56aa5 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,57 @@ +project_name: gin + +builds: + - + # If true, skip the build. + # Useful for library projects. + # Default is false + skip: true + +changelog: + # Set it to true if you wish to skip the changelog generation. + # This may result in an empty release notes on GitHub/GitLab/Gitea. + skip: false + + # Changelog generation implementation to use. + # + # Valid options are: + # - `git`: uses `git log`; + # - `github`: uses the compare GitHub API, appending the author login to the changelog. + # - `gitlab`: uses the compare GitLab API, appending the author name and email to the changelog. + # - `github-native`: uses the GitHub release notes generation API, disables the groups feature. + # + # Defaults to `git`. + use: git + + # Sorts the changelog by the commit's messages. + # Could either be asc, desc or empty + # Default is empty + sort: asc + + # Group commits messages by given regex and title. + # Order value defines the order of the groups. + # Proving no regex means all commits will be grouped under the default group. + # Groups are disabled when using github-native, as it already groups things by itself. + # + # Default is no groups. + groups: + - title: Features + regexp: "^.*feat[(\\w)]*:+.*$" + order: 0 + - title: 'Bug fixes' + regexp: "^.*fix[(\\w)]*:+.*$" + order: 1 + - title: 'Enhancements' + regexp: "^.*chore[(\\w)]*:+.*$" + order: 2 + - title: Others + order: 999 + + filters: + # Commit messages matching the regexp listed here will be removed from + # the changelog + # Default is empty + exclude: + - '^docs' + - 'CICD' + - typo diff --git a/version.go b/version.go index 40e6505b31..9b0eae22a7 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.7" +const Version = "v1.8.0" From 38eb5acc6b07eea5bf455e8d188bf79fa897c7c3 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 30 May 2022 15:16:10 +0800 Subject: [PATCH 128/346] add v1.8.0 changelog (#3160) --- AUTHORS.md | 622 +++++++++++++++++++++------------- CHANGELOG.md | 38 +++ binding/toml.go | 2 +- binding/uri.go | 2 +- render/render_msgpack_test.go | 2 +- 5 files changed, 436 insertions(+), 230 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 533204edf2..b4773ef3de 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -2,237 +2,405 @@ List of all the awesome people working to make Gin the best Web Framework in Go. ## gin 1.x series authors -**Gin Core Team:** Bo-Yi Wu (@appleboy), 田欧 (@thinkerou), Javier Provecho (@javierprovecho) +**Gin Core Team:** Bo-Yi Wu (@appleboy), thinkerou (@thinkerou), Javier Provecho (@javierprovecho) ## gin 0.x series authors **Maintainers:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho) -People and companies, who have contributed, in alphabetical order. - -**@858806258 (杰哥)** -- Fix typo in example - - -**@achedeuzot (Klemen Sever)** -- Fix newline debug printing - - -**@adammck (Adam Mckaig)** -- Add MIT license - - -**@AlexanderChen1989 (Alexander)** -- Typos in README - - -**@alexanderdidenko (Aleksandr Didenko)** -- Add support multipart/form-data - - -**@alexandernyquist (Alexander Nyquist)** -- Using template.Must to fix multiple return issue -- ★ Added support for OPTIONS verb -- ★ Setting response headers before calling WriteHeader -- Improved documentation for model binding -- ★ Added Content.Redirect() -- ★ Added tons of Unit tests - - -**@austinheap (Austin Heap)** -- Added travis CI integration - - -**@andredublin (Andre Dublin)** -- Fix typo in comment - - -**@bredov (Ludwig Valda Vasquez)** -- Fix html templating in debug mode - - -**@bluele (Jun Kimura)** -- Fixes code examples in README - - -**@chad-russell** -- ★ Support for serializing gin.H into XML - - -**@dickeyxxx (Jeff Dickey)** -- Typos in README -- Add example about serving static files - - -**@donileo (Adonis)** -- Add NoMethod handler - - -**@dutchcoders (DutchCoders)** -- ★ Fix security bug that allows client to spoof ip -- Fix typo. r.HTMLTemplates -> SetHTMLTemplate - - -**@el3ctro- (Joshua Loper)** -- Fix typo in example - - -**@ethankan (Ethan Kan)** -- Unsigned integers in binding - - -**(Evgeny Persienko)** -- Validate sub structures - - -**@frankbille (Frank Bille)** -- Add support for HTTP Realm Auth - - -**@fmd (Fareed Dudhia)** -- Fix typo. SetHTTPTemplate -> SetHTMLTemplate - - -**@ironiridis (Christopher Harrington)** -- Remove old reference - - -**@jammie-stackhouse (Jamie Stackhouse)** -- Add more shortcuts for router methods - - -**@jasonrhansen** -- Fix spelling and grammar errors in documentation - - -**@JasonSoft (Jason Lee)** -- 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 - - -**@julienschmidt (Julien Schmidt)** -- gofmt the code examples - - -**@kelcecil (Kel Cecil)** -- Fix readme typo - - -**@kyledinh (Kyle Dinh)** -- Adds RunTLS() - - -**@LinusU (Linus Unnebäck)** -- Small fixes in README - - -**@loongmxbt (Saint Asky)** -- Fix typo in example - - -**@lucas-clemente (Lucas Clemente)** -- ★ work around path.Join removing trailing slashes from routes - - -**@mattn (Yasuhiro Matsumoto)** -- Improve color logger - - -**@mdigger (Dmitry Sedykh)** -- Fixes Form binding when content-type is x-www-form-urlencoded -- No repeat call c.Writer.Status() in gin.Logger -- Fixes Content-Type for json render - - -**@mirzac (Mirza Ceric)** -- Fix debug printing - - -**@mopemope (Yutaka Matsubara)** -- ★ Adds Godep support (Dependencies Manager) -- Fix variadic parameter in the flexible render API -- Fix Corrupted plain render -- Add Pluggable View Renderer Example - - -**@msemenistyi (Mykyta Semenistyi)** -- update Readme.md. Add code to String method - - -**@msoedov (Sasha Myasoedov)** -- ★ Adds tons of unit tests. - - -**@ngerakines (Nick Gerakines)** -- ★ Improves API, c.GET() doesn't panic -- Adds MustGet() method - - -**@r8k (Rajiv Kilaparti)** -- Fix Port usage in README. - - -**@rayrod2030 (Ray Rodriguez)** -- Fix typo in example - - -**@rns** -- Fix typo in example - - -**@RobAWilkinson (Robert Wilkinson)** -- Add example of forms and params - - -**@rogierlommers (Rogier Lommers)** -- Add updated static serve example - -**@rw-access (Ross Wolf)** -- Added support to mix exact and param routes - -**@se77en (Damon Zhao)** -- Improve color logging - - -**@silasb (Silas Baronda)** -- Fixing quotes in README - - -**@SkuliOskarsson (Skuli Oskarsson)** -- Fixes some texts in README II - - -**@slimmy (Jimmy Pettersson)** -- Added messages for required bindings - - -**@smira (Andrey Smirnov)** -- Add support for ignored/unexported fields in binding - - -**@superalsrk (SRK.Lyu)** -- Update httprouter godeps - - -**@tebeka (Miki Tebeka)** -- Use net/http constants instead of numeric values - - -**@techjanitor** -- Update context.go reserved IPs - - -**@yosssi (Keiji Yoshida)** -- Fix link in README +------ +People and companies, who have contributed, in alphabetical order. -**@yuyabee** -- Fixed README +- 178inaba <178inaba@users.noreply.github.com> +- A. F +- ABHISHEK SONI +- Abhishek Chanda +- Abner Chen +- AcoNCodes +- Adam Dratwinski +- Adam Mckaig +- Adam Zielinski +- Adonis +- Alan Wang +- Albin Gilles +- Aleksandr Didenko +- Alessandro (Ale) Segala <43508+ItalyPaleAle@users.noreply.github.com> +- Alex +- Alexander +- Alexander Lokhman +- Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com> +- Alexander Nyquist +- Allen Ren +- AllinGo +- Ammar Bandukwala +- An Xiao (Luffy) +- Andre Dublin <81dublin@gmail.com> +- Andrew Szeto +- Andrey Abramov +- Andrey Nering +- Andrey Smirnov +- Andrii Bubis +- André Bazaglia +- Andy Pan +- Antoine GIRARD +- Anup Kumar Panwar <1anuppanwar@gmail.com> +- Aravinth Sundaram +- Artem +- Ashwani +- Aurelien Regat-Barrel +- Austin Heap +- Barnabus +- Bo-Yi Wu +- Boris Borshevsky +- Boyi Wu +- BradyBromley <51128276+BradyBromley@users.noreply.github.com> +- Brendan Fosberry +- Brian Wigginton +- Carlos Eduardo +- Chad Russell +- Charles +- Christian Muehlhaeuser +- Christian Persson +- Christopher Harrington +- Damon Zhao +- Dan Markham +- Dang Nguyen +- Daniel Krom +- Daniel M. Lambea +- Danieliu +- David Irvine +- David Zhang +- Davor Kapsa +- DeathKing +- Dennis Cho <47404603+forest747@users.noreply.github.com> +- Dmitry Dorogin +- Dmitry Kutakov +- Dmitry Sedykh +- Don2Quixote <35610661+Don2Quixote@users.noreply.github.com> +- Donn Pebe +- Dustin Decker +- Eason Lin +- Edward Betts +- Egor Seredin <4819888+agmt@users.noreply.github.com> +- Emmanuel Goh +- Equim +- Eren A. Akyol +- Eric_Lee +- Erik Bender +- Ethan Kan +- Evgeny Persienko +- Faisal Alam +- Fareed Dudhia +- Filip Figiel +- Florian Polster +- Frank Bille +- Franz Bettag +- Ganlv +- Gaozhen Ying +- George Gabolaev +- George Kirilenko +- Georges Varouchas +- Gordon Tyler +- Harindu Perera +- Helios <674876158@qq.com> +- Henry Kwan +- Henry Yee +- Himanshu Mishra +- Hiroyuki Tanaka +- Ibraheem Ahmed +- Ignacio Galindo +- Igor H. Vieira +- Ildar1111 <54001462+Ildar1111@users.noreply.github.com> +- Iskander (Alex) Sharipov +- Ismail Gjevori +- Ivan Chen +- JINNOUCHI Yasushi +- James Pettyjohn +- Jamie Stackhouse +- Jason Lee +- Javier Provecho +- Javier Provecho +- Javier Provecho +- Javier Provecho Fernandez +- Javier Provecho Fernandez +- Jean-Christophe Lebreton +- Jeff +- Jeremy Loy +- Jim Filippou +- Jimmy Pettersson +- John Bampton +- Johnny Dallas +- Johnny Dallas +- Jonathan (JC) Chen +- Josep Jesus Bigorra Algaba <42377845+averageflow@users.noreply.github.com> +- Josh Horowitz +- Joshua Loper +- Julien Schmidt +- Jun Kimura +- Justin Beckwith +- Justin Israel +- Justin Mayhew +- Jérôme Laforge +- Kacper Bąk <56700396+53jk1@users.noreply.github.com> +- Kamron Batman +- Kane Rogers +- Kaushik Neelichetty +- Keiji Yoshida +- Kel Cecil +- Kevin Mulvey +- Kevin Zhu +- Kirill Motkov +- Klemen Sever +- Kristoffer A. Iversen +- Krzysztof Szafrański +- Kumar McMillan +- Kyle Mcgill +- Lanco <35420416+lancoLiu@users.noreply.github.com> +- Levi Olson +- Lin Kao-Yuan +- Linus Unnebäck +- Lucas Clemente +- Ludwig Valda Vasquez +- Luis GG +- MW Lim +- Maksimov Sergey +- Manjusaka +- Manu MA +- Manu MA +- Manu Mtz-Almeida +- Manu Mtz.-Almeida +- Manuel Alonso +- Mara Kim +- Mario Kostelac +- Martin Karlsch +- Matt Newberry +- Matt Williams +- Matthieu MOREL +- Max Hilbrunner +- Maxime Soulé +- MetalBreaker +- Michael Puncel +- MichaelDeSteven <51652084+MichaelDeSteven@users.noreply.github.com> +- Mike <38686456+icy4ever@users.noreply.github.com> +- Mike Stipicevic +- Miki Tebeka +- Miles +- Mirza Ceric +- Mykyta Semenistyi +- Naoki Takano +- Ngalim Siregar +- Ni Hao +- Nick Gerakines +- Nikifor Seryakov +- Notealot <714804968@qq.com> +- Olivier Mengué +- Olivier Robardet +- Pablo Moncada +- Pablo Moncada +- Panmax <967168@qq.com> +- Peperoncino <2wua4nlyi@gmail.com> +- Philipp Meinen +- Pierre Massat +- Qt +- Quentin ROYER +- README Bot <35302948+codetriage-readme-bot@users.noreply.github.com> +- Rafal Zajac +- Rahul Datta Roy +- Rajiv Kilaparti +- Raphael Gavache +- Ray Rodriguez +- Regner Blok-Andersen +- Remco +- Rex Lee(李俊) +- Richard Lee +- Riverside +- Robert Wilkinson +- Rogier Lommers +- Rohan Pai +- Romain Beuque +- Roman Belyakovsky +- Roman Zaynetdinov <627197+zaynetro@users.noreply.github.com> +- Roman Zaynetdinov +- Ronald Petty +- Ross Wolf <31489089+rw-access@users.noreply.github.com> +- Roy Lou +- Rubi <14269809+codenoid@users.noreply.github.com> +- Ryan <46182144+ryanker@users.noreply.github.com> +- Ryan J. Yoder +- SRK.Lyu +- Sai +- Samuel Abreu +- Santhosh Kumar +- Sasha Melentyev +- Sasha Myasoedov +- Segev Finer +- Sergey Egorov +- Sergey Fedchenko +- Sergey Gonimar +- Sergey Ponomarev +- Serica <943914044@qq.com> +- Shamus Taylor +- Shilin Wang +- Shuo +- Skuli Oskarsson +- Snawoot +- Sridhar Ratnakumar +- Steeve Chailloux +- Sudhir Mishra +- Suhas Karanth +- TaeJun Park +- Tatsuya Hoshino +- Tevic +- Tevin Jeffrey +- The Gitter Badger +- Thibault Jamet +- Thomas Boerger +- Thomas Schaffer +- Tommy Chu +- Tudor Roman +- Uwe Dauernheim +- Valentine Oragbakosi +- Vas N +- Vasilyuk Vasiliy +- Victor Castell +- Vince Yuan +- Vyacheslav Dubinin +- Waynerv +- Weilin Shi <934587911@qq.com> +- Xudong Cai +- Yasuhiro Matsumoto +- Yehezkiel Syamsuhadi +- Yoshiki Nakagawa +- Yoshiyuki Kinjo +- Yue Yang +- ZYunH +- Zach Newburgh +- Zasda Yusuf Mikail +- ZhangYunHao +- ZhiFeng Hu +- Zhu Xi +- a2tt +- ahuigo <1781999+ahuigo@users.noreply.github.com> +- ali +- aljun +- andrea +- andriikushch +- anoty +- awkj +- axiaoxin <254606826@qq.com> +- bbiao +- bestgopher <84328409@qq.com> +- betahu +- bigwheel +- bn4t <17193640+bn4t@users.noreply.github.com> +- bullgare +- chainhelen +- chenyang929 +- chriswhelix +- collinmsn <4130944@qq.com> +- cssivision +- danielalves +- delphinus +- dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> +- dickeyxxx +- edebernis +- error10 +- esplo +- eudore <30709860+eudore@users.noreply.github.com> +- ffhelicopter <32922889+ffhelicopter@users.noreply.github.com> +- filikos <11477309+filikos@users.noreply.github.com> +- forging2012 +- goqihoo +- grapeVine +- guonaihong +- heige +- heige +- hellojukay +- henrylee2cn +- htobenothing +- iamhesir <78344375+iamhesir@users.noreply.github.com> +- ijaa +- ishanray +- ishanray +- itcloudy <272685110@qq.com> +- jarodsong6 +- jasonrhansen +- jincheng9 +- joeADSP <75027008+joeADSP@users.noreply.github.com> +- junfengye +- kaiiak +- kebo +- keke <19yamashita15@gmail.com> +- kishor kunal raj <68464660+kishorkunal-raj@users.noreply.github.com> +- kyledinh +- lantw44 +- likakuli <1154584512@qq.com> +- linfangrong +- linzi <873804682@qq.com> +- llgoer +- long-road <13412081338@163.com> +- mbesancon +- mehdy +- metal A-wing +- micanzhang +- minarc +- mllu +- mopemoepe +- msoedov +- mstmdev +- novaeye +- olebedev +- phithon +- pjgg +- qm012 <67568757+qm012@users.noreply.github.com> +- raymonder jin +- rns +- root@andrea:~# +- sekky0905 <20237968+sekky0905@users.noreply.github.com> +- senhtry +- shadrus +- silasb +- solos +- songjiayang +- sope +- srt180 <30768686+srt180@users.noreply.github.com> +- stackerzzq +- sunshineplan +- syssam +- techjanitor +- techjanitor +- thinkerou +- thinkgo <49174849+thinkgos@users.noreply.github.com> +- tsirolnik +- tyltr <31768692+tylitianrui@users.noreply.github.com> +- vinhha96 +- voidman +- vz +- wei +- weibaohui +- whirosan +- willnewrelic +- wssccc +- wuhuizuo +- xyb +- y-yagi +- yiranzai +- youzeliang +- yugu +- yuyabe +- zebozhuang +- zero11-0203 <93071220+zero11-0203@users.noreply.github.com> +- zesani <7sin@outlook.co.th> +- zhanweidu +- zhing +- ziheng +- zzjin +- 森 優太 <59682979+uta-mori@users.noreply.github.com> +- 杰哥 <858806258@qq.com> +- 涛叔 +- 市民233 +- 尹宝强 +- 梦溪笔谈 +- 飞雪无情 +- 寻寻觅觅的Gopher diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c806a5a7d..6d540289d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Gin ChangeLog +## Gin v1.8.0 + +### BUGFIXES + +* Fixed SetOutput() panics on go 1.17 [#2861](https://github.com/gin-gonic/gin/pull/2861) +* Fix: wrong when wildcard follows named param [#2983](https://github.com/gin-gonic/gin/pull/2983) +* Fix: missing sameSite when do context.reset() [#3123](https://github.com/gin-gonic/gin/pull/3123) + +### ENHANCEMENTS + +* Use Header() instead of deprecated HeaderMap [#2694](https://github.com/gin-gonic/gin/pull/2694) +* RouterGroup.Handle regular match optimization of http method [#2685](https://github.com/gin-gonic/gin/pull/2685) +* Add support go-json, another drop-in json replacement [#2680](https://github.com/gin-gonic/gin/pull/2680) +* Use errors.New to replace fmt.Errorf will much better [#2707](https://github.com/gin-gonic/gin/pull/2707) +* Use Duration.Truncate for truncating precision [#2711](https://github.com/gin-gonic/gin/pull/2711) +* Get client IP when using Cloudflare [#2723](https://github.com/gin-gonic/gin/pull/2723) +* Optimize code adjust [#2700](https://github.com/gin-gonic/gin/pull/2700/files) +* Optimize code and reduce code cyclomatic complexity [#2737](https://github.com/gin-gonic/gin/pull/2737) +* gin.Context with fallback value from gin.Context.Request.Context() [#2751](https://github.com/gin-gonic/gin/pull/2751) +* Improve sliceValidateError.Error performance [#2765](https://github.com/gin-gonic/gin/pull/2765) +* Support custom struct tag [#2720](https://github.com/gin-gonic/gin/pull/2720) +* Improve router group tests [#2787](https://github.com/gin-gonic/gin/pull/2787) +* Fallback Context.Deadline() Context.Done() Context.Err() to Context.Request.Context() [#2769](https://github.com/gin-gonic/gin/pull/2769) +* Some codes optimize [#2830](https://github.com/gin-gonic/gin/pull/2830) [#2834](https://github.com/gin-gonic/gin/pull/2834) [#2838](https://github.com/gin-gonic/gin/pull/2838) [#2837](https://github.com/gin-gonic/gin/pull/2837) [#2788](https://github.com/gin-gonic/gin/pull/2788) [#2848](https://github.com/gin-gonic/gin/pull/2848) [#2851](https://github.com/gin-gonic/gin/pull/2851) [#2701](https://github.com/gin-gonic/gin/pull/2701) +* TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967) +* Test(route): expose performRequest func [#3012](https://github.com/gin-gonic/gin/pull/3012) +* Support h2c with prior knowledge [#1398](https://github.com/gin-gonic/gin/pull/1398) +* Feat attachment filename support utf8 [#3071](https://github.com/gin-gonic/gin/pull/3071) +* Feat: add StaticFileFS [#2749](https://github.com/gin-gonic/gin/pull/2749) +* Feat(context): return GIN Context from Value method [#2825](https://github.com/gin-gonic/gin/pull/2825) +* Feat: automatically SetMode to TestMode when run go test [#3139](https://github.com/gin-gonic/gin/pull/3139) +* Add TOML bining for gin [#3081](https://github.com/gin-gonic/gin/pull/3081) +* IPv6 add default trusted proxies [#3033](https://github.com/gin-gonic/gin/pull/3033) + +### DOCS + +* Add note about nomsgpack tag to the readme [#2703](https://github.com/gin-gonic/gin/pull/2703) + ## Gin v1.7.7 ### BUGFIXES diff --git a/binding/toml.go b/binding/toml.go index 83b27267b1..a6b8a90abd 100644 --- a/binding/toml.go +++ b/binding/toml.go @@ -1,4 +1,4 @@ -// Copyright 2022 Gin Core Team. All rights reserved. +// Copyright 2022 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/binding/uri.go b/binding/uri.go index 4d44ab98b8..29151064a9 100644 --- a/binding/uri.go +++ b/binding/uri.go @@ -1,4 +1,4 @@ -// Copyright 2018 Gin Core Team. All rights reserved. +// Copyright 2018 Gin Core Team. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. diff --git a/render/render_msgpack_test.go b/render/render_msgpack_test.go index 7b3601a0fb..642123614e 100644 --- a/render/render_msgpack_test.go +++ b/render/render_msgpack_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. From 5fa34529aec2d81f5b2b287edfa395e2e8c8904b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 07:25:47 +0800 Subject: [PATCH 129/346] chore(deps): bump goreleaser/goreleaser-action from 2 to 3 (#3163) Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 2 to 3. - [Release notes](https://github.com/goreleaser/goreleaser-action/releases) - [Commits](https://github.com/goreleaser/goreleaser-action/compare/v2...v3) --- updated-dependencies: - dependency-name: goreleaser/goreleaser-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index e0f6e4f6fb..64ed8b2b6e 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -24,7 +24,7 @@ jobs: go-version: 1.17 - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2 + uses: goreleaser/goreleaser-action@v3 with: # either 'goreleaser' (default) or 'goreleaser-pro' distribution: goreleaser From 58303bde7da71ba6252190433b112a138e201477 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 2 Jun 2022 09:48:35 +0800 Subject: [PATCH 130/346] docs(changelog): add break changes section (#3170) * docs(changelog): add break changes section * chore: update --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d540289d1..1e7e83ee1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Gin v1.8.0 +## Break Changes + +* TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967). Please replace `RemoteIP() (net.IP, bool)` with `RemoteIP() net.IP` +* gin.Context with fallback value from gin.Context.Request.Context() [#2751](https://github.com/gin-gonic/gin/pull/2751) + ### BUGFIXES * Fixed SetOutput() panics on go 1.17 [#2861](https://github.com/gin-gonic/gin/pull/2861) @@ -18,7 +23,6 @@ * Get client IP when using Cloudflare [#2723](https://github.com/gin-gonic/gin/pull/2723) * Optimize code adjust [#2700](https://github.com/gin-gonic/gin/pull/2700/files) * Optimize code and reduce code cyclomatic complexity [#2737](https://github.com/gin-gonic/gin/pull/2737) -* gin.Context with fallback value from gin.Context.Request.Context() [#2751](https://github.com/gin-gonic/gin/pull/2751) * Improve sliceValidateError.Error performance [#2765](https://github.com/gin-gonic/gin/pull/2765) * Support custom struct tag [#2720](https://github.com/gin-gonic/gin/pull/2720) * Improve router group tests [#2787](https://github.com/gin-gonic/gin/pull/2787) From 92ba8e17aae5103b98cd6b87a300b33d31716a87 Mon Sep 17 00:00:00 2001 From: Qt Date: Thu, 2 Jun 2022 11:52:28 +0800 Subject: [PATCH 131/346] fix: typo (#3171) --- README.md | 75 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 6b4cabb41b..5cc8321b63 100644 --- a/README.md +++ b/README.md @@ -114,12 +114,16 @@ $ cat example.go ```go package main -import "github.com/gin-gonic/gin" +import ( + "net/http" + + "github.com/gin-gonic/gin" +) func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { - c.JSON(200, gin.H{ + c.JSON(http.StatusOK, gin.H{ "message": "pong", }) }) @@ -300,7 +304,7 @@ func main() { message := c.PostForm("message") nick := c.DefaultPostForm("nick", "anonymous") - c.JSON(200, gin.H{ + c.JSON(http.StatusOK, gin.H{ "status": "posted", "message": message, "nick": nick, @@ -570,7 +574,7 @@ func main() { router := gin.Default() router.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") + c.String(http.StatusOK, "pong") })    router.Run(":8080") @@ -602,7 +606,7 @@ func main() { router.Use(gin.Recovery()) router.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") + c.String(http.StatusOK, "pong") }) router.Run(":8080") @@ -630,7 +634,7 @@ func main() { router := gin.Default() router.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") + c.String(http.StatusOK, "pong") }) router.Run(":8080") @@ -649,7 +653,7 @@ func main() { router := gin.Default() router.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") + c.String(http.StatusOK, "pong") }) router.Run(":8080") @@ -848,6 +852,7 @@ package main import ( "log" + "net/http" "github.com/gin-gonic/gin" ) @@ -870,7 +875,7 @@ func startPage(c *gin.Context) { log.Println(person.Name) log.Println(person.Address) } - c.String(200, "Success") + c.String(http.StatusOK, "Success") } ``` @@ -884,6 +889,7 @@ package main import ( "log" + "net/http" "time" "github.com/gin-gonic/gin" @@ -916,7 +922,7 @@ func startPage(c *gin.Context) { log.Println(person.UnixTime) } - c.String(200, "Success") + c.String(http.StatusOK, "Success") } ``` @@ -932,7 +938,11 @@ See the [detail information](https://github.com/gin-gonic/gin/issues/846). ```go package main -import "github.com/gin-gonic/gin" +import ( + "net/http" + + "github.com/gin-gonic/gin" +) type Person struct { ID string `uri:"id" binding:"required,uuid"` @@ -944,10 +954,10 @@ func main() { route.GET("/:name/:id", func(c *gin.Context) { var person Person if err := c.ShouldBindUri(&person); err != nil { - c.JSON(400, gin.H{"msg": err.Error()}) + c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()}) return } - c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID}) + c.JSON(http.StatusOK, gin.H{"name": person.Name, "uuid": person.ID}) }) route.Run(":8088") } @@ -966,6 +976,8 @@ package main import ( "fmt" + "net/http" + "github.com/gin-gonic/gin" ) @@ -980,11 +992,11 @@ func main() { h := testHeader{} if err := c.ShouldBindHeader(&h); err != nil { - c.JSON(200, err) + c.JSON(http.StatusOK, err) } fmt.Printf("%#v\n", h) - c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain}) + c.JSON(http.StatusOK, gin.H{"Rate": h.Rate, "Domain": h.Domain}) }) r.Run() @@ -1014,7 +1026,7 @@ type myForm struct { func formHandler(c *gin.Context) { var fakeForm myForm c.ShouldBind(&fakeForm) - c.JSON(200, gin.H{"color": fakeForm.Colors}) + c.JSON(http.StatusOK, gin.H{"color": fakeForm.Colors}) } ... @@ -1219,14 +1231,14 @@ func main() { // Serves unicode entities r.GET("/json", func(c *gin.Context) { - c.JSON(200, gin.H{ + c.JSON(http.StatusOK, gin.H{ "html": "Hello, world!", }) }) // Serves literal characters r.GET("/purejson", func(c *gin.Context) { - c.PureJSON(200, gin.H{ + c.PureJSON(http.StatusOK, gin.H{ "html": "Hello, world!", }) }) @@ -1473,7 +1485,7 @@ r.GET("/test", func(c *gin.Context) { r.HandleContext(c) }) r.GET("/test2", func(c *gin.Context) { - c.JSON(200, gin.H{"hello": "world"}) + c.JSON(http.StatusOK, gin.H{"hello": "world"}) }) ``` @@ -1626,6 +1638,7 @@ package main import ( "log" + "net/http" "github.com/gin-gonic/autotls" "github.com/gin-gonic/gin" @@ -1636,7 +1649,7 @@ func main() { // Ping handler r.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") + c.String(http.StatusOK, "pong") }) log.Fatal(autotls.Run(r, "example1.com", "example2.com")) @@ -1650,6 +1663,7 @@ package main import ( "log" + "net/http" "github.com/gin-gonic/autotls" "github.com/gin-gonic/gin" @@ -1661,7 +1675,7 @@ func main() { // Ping handler r.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") + c.String(http.StatusOK, "pong") }) m := autocert.Manager{ @@ -1922,7 +1936,7 @@ type StructD struct { func GetDataB(c *gin.Context) { var b StructB c.Bind(&b) - c.JSON(200, gin.H{ + c.JSON(http.StatusOK, gin.H{ "a": b.NestedStruct, "b": b.FieldB, }) @@ -1931,7 +1945,7 @@ func GetDataB(c *gin.Context) { func GetDataC(c *gin.Context) { var b StructC c.Bind(&b) - c.JSON(200, gin.H{ + c.JSON(http.StatusOK, gin.H{ "a": b.NestedStructPointer, "c": b.FieldC, }) @@ -1940,7 +1954,7 @@ func GetDataC(c *gin.Context) { func GetDataD(c *gin.Context) { var b StructD c.Bind(&b) - c.JSON(200, gin.H{ + c.JSON(http.StatusOK, gin.H{ "x": b.NestedAnonyStruct, "d": b.FieldD, }) @@ -2090,6 +2104,7 @@ package main import ( "html/template" "log" + "net/http" "github.com/gin-gonic/gin" ) @@ -2118,7 +2133,7 @@ func main() { log.Printf("Failed to push: %v", err) } } - c.HTML(200, "https", gin.H{ + c.HTML(http.StatusOK, "https", gin.H{ "status": "success", }) }) @@ -2274,10 +2289,16 @@ The `net/http/httptest` package is preferable way for HTTP testing. ```go package main +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + func setupRouter() *gin.Engine { r := gin.Default() r.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") + c.String(http.StatusOK, "pong") }) return r } @@ -2305,10 +2326,10 @@ func TestPingRoute(t *testing.T) { router := setupRouter() w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/ping", nil) + req, _ := http.NewRequest(http.MethodGet, "/ping", nil) router.ServeHTTP(w, req) - assert.Equal(t, 200, w.Code) + assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "pong", w.Body.String()) } ``` From f197a8bae0c87e1b7cc2e32e399a40665a82f077 Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 6 Jun 2022 18:43:53 +0800 Subject: [PATCH 132/346] feat(context): add ContextWithFallback feature flag (#3166) (#3172) Enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() --- context.go | 8 ++-- context_test.go | 115 +++++++++++++++++++++++++++++++++++++++++++----- gin.go | 3 ++ 3 files changed, 110 insertions(+), 16 deletions(-) diff --git a/context.go b/context.go index 6b25b3a31d..b1ad95e623 100644 --- a/context.go +++ b/context.go @@ -1158,7 +1158,7 @@ func (c *Context) SetAccepted(formats ...string) { // Deadline returns that there is no deadline (ok==false) when c.Request has no Context. func (c *Context) Deadline() (deadline time.Time, ok bool) { - if c.Request == nil || c.Request.Context() == nil { + if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil { return } return c.Request.Context().Deadline() @@ -1166,7 +1166,7 @@ func (c *Context) Deadline() (deadline time.Time, ok bool) { // Done returns nil (chan which will wait forever) when c.Request has no Context. func (c *Context) Done() <-chan struct{} { - if c.Request == nil || c.Request.Context() == nil { + if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil { return nil } return c.Request.Context().Done() @@ -1174,7 +1174,7 @@ func (c *Context) Done() <-chan struct{} { // Err returns nil when c.Request has no Context. func (c *Context) Err() error { - if c.Request == nil || c.Request.Context() == nil { + if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil { return nil } return c.Request.Context().Err() @@ -1195,7 +1195,7 @@ func (c *Context) Value(key any) any { return val } } - if c.Request == nil || c.Request.Context() == nil { + if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil { return nil } return c.Request.Context().Value(key) diff --git a/context_test.go b/context_test.go index f9f0c1dc19..6c0be544ef 100644 --- a/context_test.go +++ b/context_test.go @@ -2097,12 +2097,18 @@ func TestRemoteIPFail(t *testing.T) { } func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) { - c := &Context{} + c, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c.engine.ContextWithFallback = true + deadline, ok := c.Deadline() assert.Zero(t, deadline) assert.False(t, ok) - c2 := &Context{} + c2, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c2.engine.ContextWithFallback = true + c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil) d := time.Now().Add(time.Second) ctx, cancel := context.WithDeadline(context.Background(), d) @@ -2114,10 +2120,16 @@ func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) { } func TestContextWithFallbackDoneFromRequestContext(t *testing.T) { - c := &Context{} + c, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c.engine.ContextWithFallback = true + assert.Nil(t, c.Done()) - c2 := &Context{} + c2, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c2.engine.ContextWithFallback = true + c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil) ctx, cancel := context.WithCancel(context.Background()) c2.Request = c2.Request.WithContext(ctx) @@ -2126,10 +2138,16 @@ func TestContextWithFallbackDoneFromRequestContext(t *testing.T) { } func TestContextWithFallbackErrFromRequestContext(t *testing.T) { - c := &Context{} + c, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c.engine.ContextWithFallback = true + assert.Nil(t, c.Err()) - c2 := &Context{} + c2, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c2.engine.ContextWithFallback = true + c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil) ctx, cancel := context.WithCancel(context.Background()) c2.Request = c2.Request.WithContext(ctx) @@ -2138,9 +2156,9 @@ func TestContextWithFallbackErrFromRequestContext(t *testing.T) { assert.EqualError(t, c2.Err(), context.Canceled.Error()) } -type contextKey string - func TestContextWithFallbackValueFromRequestContext(t *testing.T) { + type contextKey string + tests := []struct { name string getContextAndKey func() (*Context, any) @@ -2150,7 +2168,9 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { name: "c with struct context key", getContextAndKey: func() (*Context, any) { var key struct{} - c := &Context{} + c, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c.engine.ContextWithFallback = true c.Request, _ = http.NewRequest("POST", "/", nil) c.Request = c.Request.WithContext(context.WithValue(context.TODO(), key, "value")) return c, key @@ -2160,7 +2180,9 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { { name: "c with string context key", getContextAndKey: func() (*Context, any) { - c := &Context{} + c, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c.engine.ContextWithFallback = true c.Request, _ = http.NewRequest("POST", "/", nil) c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value")) return c, contextKey("key") @@ -2170,7 +2192,10 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { { name: "c with nil http.Request", getContextAndKey: func() (*Context, any) { - c := &Context{} + c, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c.engine.ContextWithFallback = true + c.Request = nil return c, "key" }, value: nil, @@ -2178,7 +2203,9 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { { name: "c with nil http.Request.Context()", getContextAndKey: func() (*Context, any) { - c := &Context{} + c, _ := CreateTestContext(httptest.NewRecorder()) + // enable ContextWithFallback feature flag + c.engine.ContextWithFallback = true c.Request, _ = http.NewRequest("POST", "/", nil) return c, "key" }, @@ -2193,6 +2220,70 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { } } +func TestContextCopyShouldNotCancel(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer srv.Close() + + ensureRequestIsOver := make(chan struct{}) + + wg := &sync.WaitGroup{} + + r := New() + r.GET("/", func(ginctx *Context) { + wg.Add(1) + + ginctx = ginctx.Copy() + + // start async goroutine for calling srv + go func() { + defer wg.Done() + + <-ensureRequestIsOver // ensure request is done + + req, err := http.NewRequestWithContext(ginctx, http.MethodGet, srv.URL, nil) + must(err) + + res, err := http.DefaultClient.Do(req) + if err != nil { + t.Error(fmt.Errorf("request error: %w", err)) + return + } + + if res.StatusCode != http.StatusOK { + t.Error(fmt.Errorf("unexpected status code: %s", res.Status)) + } + }() + }) + + l, err := net.Listen("tcp", ":0") + must(err) + go func() { + s := &http.Server{ + Handler: r, + } + + must(s.Serve(l)) + }() + + addr := strings.Split(l.Addr().String(), ":") + res, err := http.Get(fmt.Sprintf("http://127.0.0.1:%s/", addr[len(addr)-1])) + if err != nil { + t.Error(fmt.Errorf("request error: %w", err)) + return + } + + close(ensureRequestIsOver) + + if res.StatusCode != http.StatusOK { + t.Error(fmt.Errorf("unexpected status code: %s", res.Status)) + return + } + + wg.Wait() +} + func TestContextAddParam(t *testing.T) { c := &Context{} id := "id" diff --git a/gin.go b/gin.go index e516360caa..f9324299b7 100644 --- a/gin.go +++ b/gin.go @@ -147,6 +147,9 @@ type Engine struct { // UseH2C enable h2c support. UseH2C bool + // ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil. + ContextWithFallback bool + delims render.Delims secureJSONPrefix string HTMLRender render.HTMLRender From ed049dd850fb09f93c6993c829744997269a35b5 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 6 Jun 2022 21:01:40 +0800 Subject: [PATCH 133/346] docs: release v1.8.1 version (#3176) Signed-off-by: Bo-Yi Wu --- CHANGELOG.md | 6 ++++++ version.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e7e83ee1f..1bc51a8c9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Gin ChangeLog +## Gin v1.8.1 + +### ENHANCEMENTS + +* feat(context): add ContextWithFallback feature flag [#3172](https://github.com/gin-gonic/gin/pull/3172) + ## Gin v1.8.0 ## Break Changes diff --git a/version.go b/version.go index 9b0eae22a7..632ca7d1d6 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.8.0" +const Version = "v1.8.1" From f2182de38c8d19d35d4b4191749216f12512f59c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 07:55:03 +0800 Subject: [PATCH 134/346] chore(deps): bump github.com/stretchr/testify from 1.7.1 to 1.7.2 (#3177) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.1 to 1.7.2. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.7.1...v1.7.2) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 6cdde34e63..2d2c7dcb63 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/pelletier/go-toml/v2 v2.0.1 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.7.2 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 google.golang.org/protobuf v1.28.0 @@ -27,5 +27,5 @@ require ( golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect golang.org/x/text v0.3.6 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d06b810376..18233de494 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= @@ -80,5 +81,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 6c3a1d7063b35e5981af3ce2fb98fe1ee89fdcf6 Mon Sep 17 00:00:00 2001 From: Jordan Day Date: Wed, 8 Jun 2022 21:08:49 -0500 Subject: [PATCH 135/346] Small doc fix on Context's ClientIP() method. (#3180) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index b1ad95e623..46bf113348 100644 --- a/context.go +++ b/context.go @@ -748,7 +748,7 @@ func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error } // ClientIP implements one 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 calls 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 from Request.RemoteAddr) is returned. From 05caa5c00e552a27c3bfb659b99c0d79b81fafe4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 10:08:56 +0800 Subject: [PATCH 136/346] chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.1 to 2.0.2 (#3198) Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.1 to 2.0.2. - [Release notes](https://github.com/pelletier/go-toml/releases) - [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml) - [Commits](https://github.com/pelletier/go-toml/compare/v2.0.1...v2.0.2) --- updated-dependencies: - dependency-name: github.com/pelletier/go-toml/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 2d2c7dcb63..901034637c 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/goccy/go-json v0.9.7 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 - github.com/pelletier/go-toml/v2 v2.0.1 + github.com/pelletier/go-toml/v2 v2.0.2 github.com/stretchr/testify v1.7.2 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 diff --git a/go.sum b/go.sum index 18233de494..620eccec5c 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= +github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -48,7 +48,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= From 815122a0f4b95f12295921af7e04e0225eb9a6cc Mon Sep 17 00:00:00 2001 From: LanLanceYuan <92938836+L2ncE@users.noreply.github.com> Date: Wed, 15 Jun 2022 17:31:44 +0800 Subject: [PATCH 137/346] Fix a syntax error in a code comment (#3201) --- tree.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tree.go b/tree.go index 88100eec84..956bf4dd3c 100644 --- a/tree.go +++ b/tree.go @@ -455,7 +455,7 @@ walk: // Outer loop for walking the tree if !n.wildChild { // If the path at the end of the loop is not equal to '/' and the current node has no child nodes - // the current node needs to roll back to last vaild skippedNode + // the current node needs to roll back to last valid skippedNode if path != "/" { for l := len(*skippedNodes); l > 0; { skippedNode := (*skippedNodes)[l-1] @@ -572,7 +572,7 @@ walk: // Outer loop for walking the tree if path == prefix { // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node - // the current node needs to roll back to last vaild skippedNode + // the current node needs to roll back to last valid skippedNode if n.handlers == nil && path != "/" { for l := len(*skippedNodes); l > 0; { skippedNode := (*skippedNodes)[l-1] From 12b55b4fe9f8e06da3b8f0c4314b3d68a7051e9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jun 2022 22:06:46 +0800 Subject: [PATCH 138/346] chore(deps): bump github.com/stretchr/testify from 1.7.2 to 1.7.4 (#3207) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.2 to 1.7.4. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.7.2...v1.7.4) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 901034637c..f759066175 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/pelletier/go-toml/v2 v2.0.2 - github.com/stretchr/testify v1.7.2 + github.com/stretchr/testify v1.7.4 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 google.golang.org/protobuf v1.28.0 diff --git a/go.sum b/go.sum index 620eccec5c..a590d38fb8 100644 --- a/go.sum +++ b/go.sum @@ -45,11 +45,14 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= From 6de2245e6265a077326d13cb249378b2d27ad781 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 27 Jun 2022 07:11:41 +0800 Subject: [PATCH 139/346] switch min version of go to 1.15 (#3211) --- .github/workflows/gin.yml | 2 +- README.md | 2 +- debug.go | 2 +- debug_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index e8ef30ca73..6dc787a2d0 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -28,7 +28,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: [1.14, 1.15, 1.16, 1.17, 1.18] + go: [1.15, 1.16, 1.17, 1.18] test-tags: ['', nomsgpack] include: - os: ubuntu-latest diff --git a/README.md b/README.md index 5cc8321b63..2477d0b04d 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi To install Gin package, you need to install Go and set your Go workspace first. -1. You first need [Go](https://golang.org/) installed (**version 1.14+ is required**), then you can use the below Go command to install Gin. +1. You first need [Go](https://golang.org/) installed (**version 1.15+ is required**), then you can use the below Go command to install Gin. ```sh $ go get -u github.com/gin-gonic/gin diff --git a/debug.go b/debug.go index 25fd7c87f9..8367de9c10 100644 --- a/debug.go +++ b/debug.go @@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) { func debugPrintWARNINGDefault() { if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer { - debugPrint(`[WARNING] Now Gin requires Go 1.14+. + debugPrint(`[WARNING] Now Gin requires Go 1.15+. `) } diff --git a/debug_test.go b/debug_test.go index 4ac55fe148..5c29a74bb0 100644 --- a/debug_test.go +++ b/debug_test.go @@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { }) m, e := getMinVer(runtime.Version()) if e == nil && m <= ginSupportMinGoVer { - assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.14+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) + assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.15+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } else { assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } From 92dd245c9bef184fcfb5289f31f4247f9fced87b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 10:44:54 +0800 Subject: [PATCH 140/346] chore(deps): bump github.com/stretchr/testify from 1.7.4 to 1.7.5 (#3213) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.4 to 1.7.5. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.7.4...v1.7.5) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... 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 f759066175..283c6a5019 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/pelletier/go-toml/v2 v2.0.2 - github.com/stretchr/testify v1.7.4 + github.com/stretchr/testify v1.7.5 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 google.golang.org/protobuf v1.28.0 diff --git a/go.sum b/go.sum index a590d38fb8..4d6c06eabc 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= -github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= +github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= From 088cdd74d42df20d4e58aa17d3f4403a22e8a545 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Fri, 1 Jul 2022 10:31:31 +0800 Subject: [PATCH 141/346] Fix the value of ginSupportMinGoVer constant by semantic (#3221) --- debug.go | 4 ++-- debug_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/debug.go b/debug.go index 8367de9c10..b9f8234af3 100644 --- a/debug.go +++ b/debug.go @@ -12,7 +12,7 @@ import ( "strings" ) -const ginSupportMinGoVer = 14 +const ginSupportMinGoVer = 15 // IsDebugging returns true if the framework is running in debug mode. // Use SetMode(gin.ReleaseMode) to disable debug mode. @@ -66,7 +66,7 @@ func getMinVer(v string) (uint64, error) { } func debugPrintWARNINGDefault() { - if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer { + if v, e := getMinVer(runtime.Version()); e == nil && v < ginSupportMinGoVer { debugPrint(`[WARNING] Now Gin requires Go 1.15+. `) diff --git a/debug_test.go b/debug_test.go index 5c29a74bb0..bf0e6ab847 100644 --- a/debug_test.go +++ b/debug_test.go @@ -103,7 +103,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { SetMode(TestMode) }) m, e := getMinVer(runtime.Version()) - if e == nil && m <= ginSupportMinGoVer { + if e == nil && m < ginSupportMinGoVer { assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.15+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } else { assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) From 680be7d928fd120fd974a287d4205d4d5ad7c925 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Fri, 1 Jul 2022 17:38:32 +0800 Subject: [PATCH 142/346] Add some tests for YAML and TOML formats (#3223) --- context_test.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/context_test.go b/context_test.go index 6c0be544ef..d09b0ae1ea 100644 --- a/context_test.go +++ b/context_test.go @@ -1060,6 +1060,19 @@ func TestContextRenderYAML(t *testing.T) { assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type")) } +// TestContextRenderTOML tests that the response is serialized as TOML +// and Content-Type is set to application/toml +func TestContextRenderTOML(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.TOML(http.StatusCreated, H{"foo": "bar"}) + + assert.Equal(t, http.StatusCreated, w.Code) + assert.Equal(t, "foo = 'bar'\n", w.Body.String()) + assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type")) +} + // TestContextRenderProtoBuf tests that the response is serialized as ProtoBuf // and Content-Type is set to application/x-protobuf // and we just use the example protobuf to check if the response is correct @@ -1180,6 +1193,36 @@ func TestContextNegotiationWithXML(t *testing.T) { assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type")) } +func TestContextNegotiationWithYAML(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + c.Request, _ = http.NewRequest("POST", "", nil) + + c.Negotiate(http.StatusOK, Negotiate{ + Offered: []string{MIMEYAML, MIMEXML, MIMEJSON, MIMETOML}, + Data: H{"foo": "bar"}, + }) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "foo: bar\n", w.Body.String()) + assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type")) +} + +func TestContextNegotiationWithTOML(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + c.Request, _ = http.NewRequest("POST", "", nil) + + c.Negotiate(http.StatusOK, Negotiate{ + Offered: []string{MIMETOML, MIMEXML, MIMEJSON, MIMEYAML}, + Data: H{"foo": "bar"}, + }) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "foo = 'bar'\n", w.Body.String()) + assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type")) +} + func TestContextNegotiationWithHTML(t *testing.T) { w := httptest.NewRecorder() c, router := CreateTestContext(w) @@ -1640,6 +1683,23 @@ func TestContextBindWithYAML(t *testing.T) { assert.Equal(t, 0, w.Body.Len()) } +func TestContextBindWithTOML(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo = 'bar'\nbar = 'foo'")) + c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type + + var obj struct { + Foo string `toml:"foo"` + Bar string `toml:"bar"` + } + assert.NoError(t, c.BindTOML(&obj)) + assert.Equal(t, "foo", obj.Bar) + assert.Equal(t, "bar", obj.Foo) + assert.Equal(t, 0, w.Body.Len()) +} + func TestContextBadAutoBind(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) From e837e1cd1850559d91d921b712bc7b0c8f78cf7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 09:56:54 +0800 Subject: [PATCH 143/346] chore(deps): bump github.com/stretchr/testify from 1.7.5 to 1.8.0 (#3229) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.5 to 1.8.0. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.7.5...v1.8.0) --- updated-dependencies: - dependency-name: github.com/stretchr/testify 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 283c6a5019..6371d226f8 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/pelletier/go-toml/v2 v2.0.2 - github.com/stretchr/testify v1.7.5 + github.com/stretchr/testify v1.8.0 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 google.golang.org/protobuf v1.28.0 diff --git a/go.sum b/go.sum index 4d6c06eabc..aba0019912 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= -github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= From b57163a0e4339d7feb393ff430a454f4e448cf9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 09:58:06 +0800 Subject: [PATCH 144/346] chore(deps): bump github.com/goccy/go-json from 0.9.7 to 0.9.8 (#3228) Bumps [github.com/goccy/go-json](https://github.com/goccy/go-json) from 0.9.7 to 0.9.8. - [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.9.7...v0.9.8) --- updated-dependencies: - dependency-name: github.com/goccy/go-json dependency-type: direct:production update-type: version-update:semver-patch ... 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 6371d226f8..ae7a5f8177 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.10.0 - github.com/goccy/go-json v0.9.7 + github.com/goccy/go-json v0.9.8 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/pelletier/go-toml/v2 v2.0.2 diff --git a/go.sum b/go.sum index aba0019912..3529342cad 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.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= -github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.8 h1:DxXB6MLd6yyel7CLph8EwNIonUtVZd3Ue5iRcL4DQCE= +github.com/goccy/go-json v0.9.8/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 c35bde97d5380b48e7736742c3477c08c68047df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 31 Jul 2022 13:02:22 +0800 Subject: [PATCH 145/346] chore(deps): bump github.com/goccy/go-json from 0.9.8 to 0.9.10 (#3251) 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 ae7a5f8177..0b259e132c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.10.0 - github.com/goccy/go-json v0.9.8 + github.com/goccy/go-json v0.9.10 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.14 github.com/pelletier/go-toml/v2 v2.0.2 diff --git a/go.sum b/go.sum index 3529342cad..640ed10e4d 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.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/goccy/go-json v0.9.8 h1:DxXB6MLd6yyel7CLph8EwNIonUtVZd3Ue5iRcL4DQCE= -github.com/goccy/go-json v0.9.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc= +github.com/goccy/go-json v0.9.10/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 79dd72deb9edd7120bd0eda36e99f9bfb712d818 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 1 Aug 2022 09:23:45 +0800 Subject: [PATCH 146/346] docs: update markdown format (#3260) Signed-off-by: Bo-Yi Wu --- README.md | 1876 +++++++++++++++++++++++++++-------------------------- 1 file changed, 946 insertions(+), 930 deletions(-) diff --git a/README.md b/README.md index 2477d0b04d..1c315f8869 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. - ## Contents - [Gin Web Framework](#gin-web-framework) @@ -23,7 +22,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [Quick start](#quick-start) - [Benchmarks](#benchmarks) - [Gin v1. stable](#gin-v1-stable) - - [Build with jsoniter/go-json](#build-with-json-replacement) + - [Build with json replacement](#build-with-json-replacement) - [Build without `MsgPack` rendering feature](#build-without-msgpack-rendering-feature) - [API Examples](#api-examples) - [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options) @@ -38,6 +37,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [Grouping routes](#grouping-routes) - [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default) - [Using middleware](#using-middleware) + - [Custom Recovery behavior](#custom-recovery-behavior) - [How to write log file](#how-to-write-log-file) - [Custom Log Format](#custom-log-format) - [Controlling Log output coloring](#controlling-log-output-coloring) @@ -75,6 +75,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [Build a single binary with templates](#build-a-single-binary-with-templates) - [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct) - [Try to bind body into different structs](#try-to-bind-body-into-different-structs) + - [Bind form-data request with custom struct and custom tag](#bind-form-data-request-with-custom-struct-and-custom-tag) - [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) @@ -89,7 +90,7 @@ To install Gin package, you need to install Go and set your Go workspace first. 1. You first need [Go](https://golang.org/) installed (**version 1.15+ is required**), then you can use the below Go command to install Gin. ```sh -$ go get -u github.com/gin-gonic/gin +go get -u github.com/gin-gonic/gin ``` 2. Import it in your code: @@ -115,19 +116,19 @@ $ cat example.go package main import ( - "net/http" + "net/http" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) func main() { - r := gin.Default() - r.GET("/ping", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "pong", - }) - }) - r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") + r := gin.Default() + r.GET("/ping", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "pong", + }) + }) + r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") } ``` @@ -193,12 +194,15 @@ Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httpr Gin uses `encoding/json` as default json package but you can change it by build from other tags. [jsoniter](https://github.com/json-iterator/go) + ```sh -$ go build -tags=jsoniter . +go build -tags=jsoniter . ``` + [go-json](https://github.com/goccy/go-json) + ```sh -$ go build -tags=go_json . +go build -tags=go_json . ``` ## Build without `MsgPack` rendering feature @@ -206,7 +210,7 @@ $ go build -tags=go_json . Gin enables `MsgPack` rendering feature by default. But you can disable this feature by specifying `nomsgpack` build tag. ```sh -$ go build -tags=nomsgpack . +go build -tags=nomsgpack . ``` This is useful to reduce the binary size of executable files. See the [detail information](https://github.com/gin-gonic/gin/pull/1852). @@ -219,22 +223,22 @@ You can find a number of ready-to-run examples at [Gin examples repository](http ```go func main() { - // Creates a gin router with default middleware: - // logger and recovery (crash-free) middleware - router := gin.Default() + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() - router.GET("/someGet", getting) - router.POST("/somePost", posting) - router.PUT("/somePut", putting) - router.DELETE("/someDelete", deleting) - router.PATCH("/somePatch", patching) - router.HEAD("/someHead", head) - router.OPTIONS("/someOptions", options) + router.GET("/someGet", getting) + router.POST("/somePost", posting) + router.PUT("/somePut", putting) + router.DELETE("/someDelete", deleting) + router.PATCH("/somePatch", patching) + router.HEAD("/someHead", head) + router.OPTIONS("/someOptions", options) - // By default it serves on :8080 unless a - // PORT environment variable was defined. - router.Run() - // router.Run(":3000") for a hard coded port + // By default it serves on :8080 unless a + // PORT environment variable was defined. + router.Run() + // router.Run(":3000") for a hard coded port } ``` @@ -242,37 +246,37 @@ func main() { ```go func main() { - router := gin.Default() + router := gin.Default() - // This handler will match /user/john but will not match /user/ or /user - router.GET("/user/:name", func(c *gin.Context) { - name := c.Param("name") - c.String(http.StatusOK, "Hello %s", name) - }) + // This handler will match /user/john but will not match /user/ or /user + router.GET("/user/:name", func(c *gin.Context) { + name := c.Param("name") + c.String(http.StatusOK, "Hello %s", name) + }) - // However, this one will match /user/john/ and also /user/john/send - // If no other routers match /user/john, it will redirect to /user/john/ - router.GET("/user/:name/*action", func(c *gin.Context) { - name := c.Param("name") - action := c.Param("action") - message := name + " is " + action - c.String(http.StatusOK, message) - }) + // However, this one will match /user/john/ and also /user/john/send + // If no other routers match /user/john, it will redirect to /user/john/ + router.GET("/user/:name/*action", func(c *gin.Context) { + name := c.Param("name") + action := c.Param("action") + message := name + " is " + action + c.String(http.StatusOK, message) + }) - // For each matched request Context will hold the route definition - router.POST("/user/:name/*action", func(c *gin.Context) { - b := c.FullPath() == "/user/:name/*action" // true - c.String(http.StatusOK, "%t", b) - }) + // For each matched request Context will hold the route definition + router.POST("/user/:name/*action", func(c *gin.Context) { + b := c.FullPath() == "/user/:name/*action" // true + c.String(http.StatusOK, "%t", b) + }) - // This handler will add a new router for /user/groups. - // Exact routes are resolved before param routes, regardless of the order they were defined. - // Routes starting with /user/groups are never interpreted as /user/:name/... routes - router.GET("/user/groups", func(c *gin.Context) { - c.String(http.StatusOK, "The available groups are [...]") - }) + // This handler will add a new router for /user/groups. + // Exact routes are resolved before param routes, regardless of the order they were defined. + // Routes starting with /user/groups are never interpreted as /user/:name/... routes + router.GET("/user/groups", func(c *gin.Context) { + c.String(http.StatusOK, "The available groups are [...]") + }) - router.Run(":8080") + router.Run(":8080") } ``` @@ -280,17 +284,17 @@ func main() { ```go func main() { - router := gin.Default() + router := gin.Default() - // Query string parameters are parsed using the existing underlying request object. - // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe - router.GET("/welcome", func(c *gin.Context) { - firstname := c.DefaultQuery("firstname", "Guest") - lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") + // Query string parameters are parsed using the existing underlying request object. + // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe + router.GET("/welcome", func(c *gin.Context) { + firstname := c.DefaultQuery("firstname", "Guest") + lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") - c.String(http.StatusOK, "Hello %s %s", firstname, lastname) - }) - router.Run(":8080") + c.String(http.StatusOK, "Hello %s %s", firstname, lastname) + }) + router.Run(":8080") } ``` @@ -298,25 +302,25 @@ func main() { ```go func main() { - router := gin.Default() + router := gin.Default() - router.POST("/form_post", func(c *gin.Context) { - message := c.PostForm("message") - nick := c.DefaultPostForm("nick", "anonymous") + router.POST("/form_post", func(c *gin.Context) { + message := c.PostForm("message") + nick := c.DefaultPostForm("nick", "anonymous") - c.JSON(http.StatusOK, gin.H{ - "status": "posted", - "message": message, - "nick": nick, - }) - }) - router.Run(":8080") + c.JSON(http.StatusOK, gin.H{ + "status": "posted", + "message": message, + "nick": nick, + }) + }) + router.Run(":8080") } ``` ### Another example: query + post form -``` +```sh POST /post?id=1234&page=1 HTTP/1.1 Content-Type: application/x-www-form-urlencoded @@ -325,28 +329,28 @@ name=manu&message=this_is_great ```go func main() { - router := gin.Default() + router := gin.Default() - router.POST("/post", func(c *gin.Context) { + router.POST("/post", func(c *gin.Context) { - id := c.Query("id") - page := c.DefaultQuery("page", "0") - name := c.PostForm("name") - message := c.PostForm("message") + id := c.Query("id") + page := c.DefaultQuery("page", "0") + name := c.PostForm("name") + message := c.PostForm("message") - fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) - }) - router.Run(":8080") + fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) + }) + router.Run(":8080") } ``` -``` +```sh id: 1234; page: 1; name: manu; message: this_is_great ``` ### Map as querystring or postform parameters -``` +```sh POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1 Content-Type: application/x-www-form-urlencoded @@ -355,20 +359,20 @@ names[first]=thinkerou&names[second]=tianou ```go func main() { - router := gin.Default() + router := gin.Default() - router.POST("/post", func(c *gin.Context) { + router.POST("/post", func(c *gin.Context) { - ids := c.QueryMap("ids") - names := c.PostFormMap("names") + ids := c.QueryMap("ids") + names := c.PostFormMap("names") - fmt.Printf("ids: %v; names: %v", ids, names) - }) - router.Run(":8080") + fmt.Printf("ids: %v; names: %v", ids, names) + }) + router.Run(":8080") } ``` -``` +```sh ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou] ``` @@ -384,20 +388,20 @@ References issue [#774](https://github.com/gin-gonic/gin/issues/774) and detail ```go func main() { - router := gin.Default() - // 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 - file, _ := c.FormFile("file") - log.Println(file.Filename) + router := gin.Default() + // 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 + file, _ := c.FormFile("file") + log.Println(file.Filename) - // Upload the file to specific dst. - c.SaveUploadedFile(file, dst) + // Upload the file to specific dst. + c.SaveUploadedFile(file, dst) - c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) - }) - router.Run(":8080") + c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) + }) + router.Run(":8080") } ``` @@ -415,23 +419,23 @@ See the detail [example code](https://github.com/gin-gonic/examples/tree/master/ ```go func main() { - router := gin.Default() - // 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) { - // Multipart form - form, _ := c.MultipartForm() - files := form.File["upload[]"] - - for _, file := range files { - log.Println(file.Filename) - - // Upload the file to specific dst. - c.SaveUploadedFile(file, dst) - } - c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) - }) - router.Run(":8080") + router := gin.Default() + // 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) { + // Multipart form + form, _ := c.MultipartForm() + files := form.File["upload[]"] + + for _, file := range files { + log.Println(file.Filename) + + // Upload the file to specific dst. + c.SaveUploadedFile(file, dst) + } + c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) + }) + router.Run(":8080") } ``` @@ -448,25 +452,25 @@ curl -X POST http://localhost:8080/upload \ ```go func main() { - router := gin.Default() - - // Simple group: v1 - v1 := router.Group("/v1") - { - v1.POST("/login", loginEndpoint) - v1.POST("/submit", submitEndpoint) - v1.POST("/read", readEndpoint) - } + router := gin.Default() + + // Simple group: v1 + v1 := router.Group("/v1") + { + v1.POST("/login", loginEndpoint) + v1.POST("/submit", submitEndpoint) + v1.POST("/read", readEndpoint) + } - // Simple group: v2 - v2 := router.Group("/v2") - { - v2.POST("/login", loginEndpoint) - v2.POST("/submit", submitEndpoint) - v2.POST("/read", readEndpoint) - } + // Simple group: v2 + v2 := router.Group("/v2") + { + v2.POST("/login", loginEndpoint) + v2.POST("/submit", submitEndpoint) + v2.POST("/read", readEndpoint) + } - router.Run(":8080") + router.Run(":8080") } ``` @@ -485,81 +489,83 @@ instead of r := gin.Default() ``` - ### Using middleware + ```go func main() { - // Creates a router without any middleware by default - r := gin.New() - - // Global middleware - // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. - // By default gin.DefaultWriter = os.Stdout - r.Use(gin.Logger()) - - // Recovery middleware recovers from any panics and writes a 500 if there was one. - r.Use(gin.Recovery()) - - // Per route middleware, you can add as many as you desire. - r.GET("/benchmark", MyBenchLogger(), benchEndpoint) - - // Authorization group - // authorized := r.Group("/", AuthRequired()) - // exactly the same as: - authorized := r.Group("/") - // per group middleware! in this case we use the custom created - // AuthRequired() middleware just in the "authorized" group. - authorized.Use(AuthRequired()) - { - authorized.POST("/login", loginEndpoint) - authorized.POST("/submit", submitEndpoint) - authorized.POST("/read", readEndpoint) - - // nested group - testing := authorized.Group("testing") - // visit 0.0.0.0:8080/testing/analytics - testing.GET("/analytics", analyticsEndpoint) - } + // Creates a router without any middleware by default + r := gin.New() + + // Global middleware + // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. + // By default gin.DefaultWriter = os.Stdout + r.Use(gin.Logger()) + + // Recovery middleware recovers from any panics and writes a 500 if there was one. + r.Use(gin.Recovery()) + + // Per route middleware, you can add as many as you desire. + r.GET("/benchmark", MyBenchLogger(), benchEndpoint) + + // Authorization group + // authorized := r.Group("/", AuthRequired()) + // exactly the same as: + authorized := r.Group("/") + // per group middleware! in this case we use the custom created + // AuthRequired() middleware just in the "authorized" group. + authorized.Use(AuthRequired()) + { + authorized.POST("/login", loginEndpoint) + authorized.POST("/submit", submitEndpoint) + authorized.POST("/read", readEndpoint) + + // nested group + testing := authorized.Group("testing") + // visit 0.0.0.0:8080/testing/analytics + testing.GET("/analytics", analyticsEndpoint) + } - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") } ``` ### Custom Recovery behavior + ```go func main() { - // Creates a router without any middleware by default - r := gin.New() - - // Global middleware - // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. - // By default gin.DefaultWriter = os.Stdout - r.Use(gin.Logger()) - - // Recovery middleware recovers from any panics and writes a 500 if there was one. - r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) { - if err, ok := recovered.(string); ok { - c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) - } - c.AbortWithStatus(http.StatusInternalServerError) - })) + // Creates a router without any middleware by default + r := gin.New() + + // Global middleware + // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. + // By default gin.DefaultWriter = os.Stdout + r.Use(gin.Logger()) + + // Recovery middleware recovers from any panics and writes a 500 if there was one. + r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) { + if err, ok := recovered.(string); ok { + c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) + } + c.AbortWithStatus(http.StatusInternalServerError) + })) - r.GET("/panic", func(c *gin.Context) { - // panic with a string -- the custom middleware could save this to a database or report it to the user - panic("foo") - }) + r.GET("/panic", func(c *gin.Context) { + // panic with a string -- the custom middleware could save this to a database or report it to the user + panic("foo") + }) - r.GET("/", func(c *gin.Context) { - c.String(http.StatusOK, "ohai") - }) + r.GET("/", func(c *gin.Context) { + c.String(http.StatusOK, "ohai") + }) - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") } ``` ### How to write log file + ```go func main() { // Disable Console Color, you don't need console color when writing the logs to file. @@ -582,39 +588,41 @@ func main() { ``` ### Custom Log Format + ```go func main() { - router := gin.New() + router := gin.New() - // LoggerWithFormatter middleware will write the logs to gin.DefaultWriter - // By default gin.DefaultWriter = os.Stdout - router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { + // LoggerWithFormatter middleware will write the logs to gin.DefaultWriter + // By default gin.DefaultWriter = os.Stdout + router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { - // your custom format - return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", - param.ClientIP, - param.TimeStamp.Format(time.RFC1123), - param.Method, - param.Path, - param.Request.Proto, - param.StatusCode, - param.Latency, - param.Request.UserAgent(), - param.ErrorMessage, - ) - })) - router.Use(gin.Recovery()) + // your custom format + return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", + param.ClientIP, + param.TimeStamp.Format(time.RFC1123), + param.Method, + param.Path, + param.Request.Proto, + param.StatusCode, + param.Latency, + param.Request.UserAgent(), + param.ErrorMessage, + ) + })) + router.Use(gin.Recovery()) - router.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) + router.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) - router.Run(":8080") + router.Run(":8080") } ``` -**Sample Output** -``` +Sample Output + +```sh ::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" " ``` @@ -669,6 +677,7 @@ Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/vali Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`. Also, Gin provides two sets of methods for binding: + - **Type** - Must bind - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader`, `BindTOML` - **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method. @@ -683,74 +692,75 @@ You can also specify that specific fields are required. If a field is decorated ```go // Binding from JSON type Login struct { - User string `form:"user" json:"user" xml:"user" binding:"required"` - Password string `form:"password" json:"password" xml:"password" binding:"required"` + User string `form:"user" json:"user" xml:"user" binding:"required"` + Password string `form:"password" json:"password" xml:"password" binding:"required"` } func main() { - router := gin.Default() - - // Example for binding JSON ({"user": "manu", "password": "123"}) - router.POST("/loginJSON", func(c *gin.Context) { - var json Login - if err := c.ShouldBindJSON(&json); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - if json.User != "manu" || json.Password != "123" { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - return - } - - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - }) - - // Example for binding XML ( - // - // - // manu - // 123 - // ) - router.POST("/loginXML", func(c *gin.Context) { - var xml Login - if err := c.ShouldBindXML(&xml); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - if xml.User != "manu" || xml.Password != "123" { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - return - } - - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - }) - - // Example for binding a HTML form (user=manu&password=123) - router.POST("/loginForm", func(c *gin.Context) { - var form Login - // This will infer what binder to use depending on the content-type header. - if err := c.ShouldBind(&form); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - if form.User != "manu" || form.Password != "123" { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - return - } - - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - }) - - // Listen and serve on 0.0.0.0:8080 - router.Run(":8080") -} -``` - -**Sample request** -```shell + router := gin.Default() + + // Example for binding JSON ({"user": "manu", "password": "123"}) + router.POST("/loginJSON", func(c *gin.Context) { + var json Login + if err := c.ShouldBindJSON(&json); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if json.User != "manu" || json.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Example for binding XML ( + // + // + // manu + // 123 + // ) + router.POST("/loginXML", func(c *gin.Context) { + var xml Login + if err := c.ShouldBindXML(&xml); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if xml.User != "manu" || xml.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Example for binding a HTML form (user=manu&password=123) + router.POST("/loginForm", func(c *gin.Context) { + var form Login + // This will infer what binder to use depending on the content-type header. + if err := c.ShouldBind(&form); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if form.User != "manu" || form.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Listen and serve on 0.0.0.0:8080 + router.Run(":8080") +} +``` + +Sample request + +```sh $ curl -v -X POST \ http://localhost:8080/loginJSON \ -H 'content-type: application/json' \ @@ -771,9 +781,7 @@ $ curl -v -X POST \ {"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"} ``` -**Skip validate** - -When running the above example using the above the `curl` command, it returns error. Because the example use `binding:"required"` for `Password`. If use `binding:"-"` for `Password`, then it will not return error when running the above example again. +Skip validate: when running the above example using the above the `curl` command, it returns error. Because the example use `binding:"required"` for `Password`. If use `binding:"-"` for `Password`, then it will not return error when running the above example again. ### Custom Validators @@ -783,49 +791,49 @@ It is also possible to register custom validators. See the [example code](https: package main import ( - "net/http" - "time" + "net/http" + "time" - "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/binding" - "github.com/go-playground/validator/v10" + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" ) // Booking contains binded and validated data. type Booking struct { - CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` - CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"` + CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` + CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"` } var bookableDate validator.Func = func(fl validator.FieldLevel) bool { - date, ok := fl.Field().Interface().(time.Time) - if ok { - today := time.Now() - if today.After(date) { - return false - } - } - return true + date, ok := fl.Field().Interface().(time.Time) + if ok { + today := time.Now() + if today.After(date) { + return false + } + } + return true } func main() { - route := gin.Default() + route := gin.Default() - if v, ok := binding.Validator.Engine().(*validator.Validate); ok { - v.RegisterValidation("bookabledate", bookableDate) - } + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + v.RegisterValidation("bookabledate", bookableDate) + } - route.GET("/bookable", getBookable) - route.Run(":8085") + route.GET("/bookable", getBookable) + route.Run(":8085") } func getBookable(c *gin.Context) { - var b Booking - if err := c.ShouldBindWith(&b, binding.Query); err == nil { - c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"}) - } else { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - } + var b Booking + if err := c.ShouldBindWith(&b, binding.Query); err == nil { + c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"}) + } else { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + } } ``` @@ -851,31 +859,31 @@ See the [struct-lvl-validation example](https://github.com/gin-gonic/examples/tr package main import ( - "log" - "net/http" + "log" + "net/http" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) type Person struct { - Name string `form:"name"` - Address string `form:"address"` + Name string `form:"name"` + Address string `form:"address"` } func main() { - route := gin.Default() - route.Any("/testing", startPage) - route.Run(":8085") + route := gin.Default() + route.Any("/testing", startPage) + route.Run(":8085") } func startPage(c *gin.Context) { - var person Person - if c.ShouldBindQuery(&person) == nil { - log.Println("====== Only Bind By Query String ======") - log.Println(person.Name) - log.Println(person.Address) - } - c.String(http.StatusOK, "Success") + var person Person + if c.ShouldBindQuery(&person) == nil { + log.Println("====== Only Bind By Query String ======") + log.Println(person.Name) + log.Println(person.Address) + } + c.String(http.StatusOK, "Success") } ``` @@ -888,11 +896,11 @@ See the [detail information](https://github.com/gin-gonic/gin/issues/742#issueco package main import ( - "log" - "net/http" - "time" + "log" + "net/http" + "time" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) type Person struct { @@ -904,16 +912,16 @@ type Person struct { } func main() { - route := gin.Default() - route.GET("/testing", startPage) - route.Run(":8085") + route := gin.Default() + route.GET("/testing", startPage) + route.Run(":8085") } 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#L88 + 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#L88 if c.ShouldBind(&person) == nil { log.Println(person.Name) log.Println(person.Address) @@ -922,13 +930,14 @@ func startPage(c *gin.Context) { log.Println(person.UnixTime) } - c.String(http.StatusOK, "Success") + c.String(http.StatusOK, "Success") } ``` Test it with: + ```sh -$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033" +curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033" ``` ### Bind Uri @@ -939,34 +948,35 @@ See the [detail information](https://github.com/gin-gonic/gin/issues/846). package main import ( - "net/http" + "net/http" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) type Person struct { - ID string `uri:"id" binding:"required,uuid"` - Name string `uri:"name" binding:"required"` + ID string `uri:"id" binding:"required,uuid"` + Name string `uri:"name" binding:"required"` } func main() { - route := gin.Default() - route.GET("/:name/:id", func(c *gin.Context) { - var person Person - if err := c.ShouldBindUri(&person); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()}) - return - } - c.JSON(http.StatusOK, gin.H{"name": person.Name, "uuid": person.ID}) - }) - route.Run(":8088") + route := gin.Default() + route.GET("/:name/:id", func(c *gin.Context) { + var person Person + if err := c.ShouldBindUri(&person); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"name": person.Name, "uuid": person.ID}) + }) + route.Run(":8088") } ``` Test it with: + ```sh -$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3 -$ curl -v localhost:8088/thinkerou/not-uuid +curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3 +curl -v localhost:8088/thinkerou/not-uuid ``` ### Bind Header @@ -975,31 +985,31 @@ $ curl -v localhost:8088/thinkerou/not-uuid package main import ( - "fmt" - "net/http" + "fmt" + "net/http" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) type testHeader struct { - Rate int `header:"Rate"` - Domain string `header:"Domain"` + Rate int `header:"Rate"` + Domain string `header:"Domain"` } func main() { - r := gin.Default() - r.GET("/", func(c *gin.Context) { - h := testHeader{} + r := gin.Default() + r.GET("/", func(c *gin.Context) { + h := testHeader{} - if err := c.ShouldBindHeader(&h); err != nil { - c.JSON(http.StatusOK, err) - } + if err := c.ShouldBindHeader(&h); err != nil { + c.JSON(http.StatusOK, err) + } - fmt.Printf("%#v\n", h) - c.JSON(http.StatusOK, gin.H{"Rate": h.Rate, "Domain": h.Domain}) - }) + fmt.Printf("%#v\n", h) + c.JSON(http.StatusOK, gin.H{"Rate": h.Rate, "Domain": h.Domain}) + }) - r.Run() + r.Run() // client // curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/ @@ -1050,7 +1060,7 @@ form.html result: -``` +```json {"color":["red","green","blue"]} ``` @@ -1058,94 +1068,95 @@ result: ```go type ProfileForm struct { - Name string `form:"name" binding:"required"` - Avatar *multipart.FileHeader `form:"avatar" binding:"required"` + Name string `form:"name" binding:"required"` + Avatar *multipart.FileHeader `form:"avatar" binding:"required"` - // or for multiple files - // Avatars []*multipart.FileHeader `form:"avatar" binding:"required"` + // or for multiple files + // Avatars []*multipart.FileHeader `form:"avatar" binding:"required"` } func main() { - router := gin.Default() - router.POST("/profile", func(c *gin.Context) { - // you can bind multipart form with explicit binding declaration: - // c.ShouldBindWith(&form, binding.Form) - // or you can simply use autobinding with ShouldBind method: - var form ProfileForm - // in this case proper binding will be automatically selected - if err := c.ShouldBind(&form); err != nil { - c.String(http.StatusBadRequest, "bad request") - return - } + router := gin.Default() + router.POST("/profile", func(c *gin.Context) { + // you can bind multipart form with explicit binding declaration: + // c.ShouldBindWith(&form, binding.Form) + // or you can simply use autobinding with ShouldBind method: + var form ProfileForm + // in this case proper binding will be automatically selected + if err := c.ShouldBind(&form); err != nil { + c.String(http.StatusBadRequest, "bad request") + return + } - err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename) - if err != nil { - c.String(http.StatusInternalServerError, "unknown error") - return - } + err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename) + if err != nil { + c.String(http.StatusInternalServerError, "unknown error") + return + } - // db.Save(&form) + // db.Save(&form) - c.String(http.StatusOK, "ok") - }) - router.Run(":8080") + c.String(http.StatusOK, "ok") + }) + router.Run(":8080") } ``` Test it with: + ```sh -$ curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile +curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile ``` ### XML, JSON, YAML and ProtoBuf rendering ```go func main() { - r := gin.Default() - - // gin.H is a shortcut for map[string]interface{} - r.GET("/someJSON", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) - }) - - r.GET("/moreJSON", func(c *gin.Context) { - // You also can use a struct - var msg struct { - Name string `json:"user"` - Message string - Number int - } - msg.Name = "Lena" - msg.Message = "hey" - msg.Number = 123 - // Note that msg.Name becomes "user" in the JSON - // Will output : {"user": "Lena", "Message": "hey", "Number": 123} - c.JSON(http.StatusOK, msg) - }) - - r.GET("/someXML", func(c *gin.Context) { - c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) - }) - - r.GET("/someYAML", func(c *gin.Context) { - c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) - }) - - r.GET("/someProtoBuf", func(c *gin.Context) { - reps := []int64{int64(1), int64(2)} - label := "test" - // The specific definition of protobuf is written in the testdata/protoexample file. - data := &protoexample.Test{ - Label: &label, - Reps: reps, - } - // Note that data becomes binary data in the response - // Will output protoexample.Test protobuf serialized data - c.ProtoBuf(http.StatusOK, data) - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") + r := gin.Default() + + // gin.H is a shortcut for map[string]interface{} + r.GET("/someJSON", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/moreJSON", func(c *gin.Context) { + // You also can use a struct + var msg struct { + Name string `json:"user"` + Message string + Number int + } + msg.Name = "Lena" + msg.Message = "hey" + msg.Number = 123 + // Note that msg.Name becomes "user" in the JSON + // Will output : {"user": "Lena", "Message": "hey", "Number": 123} + c.JSON(http.StatusOK, msg) + }) + + r.GET("/someXML", func(c *gin.Context) { + c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/someYAML", func(c *gin.Context) { + c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/someProtoBuf", func(c *gin.Context) { + reps := []int64{int64(1), int64(2)} + label := "test" + // The specific definition of protobuf is written in the testdata/protoexample file. + data := &protoexample.Test{ + Label: &label, + Reps: reps, + } + // Note that data becomes binary data in the response + // Will output protoexample.Test protobuf serialized data + c.ProtoBuf(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") } ``` @@ -1155,42 +1166,43 @@ Using SecureJSON to prevent json hijacking. Default prepends `"while(1),"` to re ```go func main() { - r := gin.Default() + r := gin.Default() - // You can also use your own secure json prefix - // r.SecureJsonPrefix(")]}',\n") + // You can also use your own secure json prefix + // r.SecureJsonPrefix(")]}',\n") - r.GET("/someJSON", func(c *gin.Context) { - names := []string{"lena", "austin", "foo"} + r.GET("/someJSON", func(c *gin.Context) { + names := []string{"lena", "austin", "foo"} - // Will output : while(1);["lena","austin","foo"] - c.SecureJSON(http.StatusOK, names) - }) + // Will output : while(1);["lena","austin","foo"] + c.SecureJSON(http.StatusOK, names) + }) - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") } ``` + #### JSONP Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists. ```go func main() { - r := gin.Default() + r := gin.Default() - r.GET("/JSONP", func(c *gin.Context) { - data := gin.H{ - "foo": "bar", - } + r.GET("/JSONP", func(c *gin.Context) { + data := gin.H{ + "foo": "bar", + } - //callback is x - // Will output : x({\"foo\":\"bar\"}) - c.JSONP(http.StatusOK, data) - }) + //callback is x + // Will output : x({\"foo\":\"bar\"}) + c.JSONP(http.StatusOK, data) + }) - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") // client // curl http://127.0.0.1:8080/JSONP?callback=x @@ -1203,20 +1215,20 @@ Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII characters. ```go func main() { - r := gin.Default() + r := gin.Default() - r.GET("/someJSON", func(c *gin.Context) { - data := gin.H{ - "lang": "GO语言", - "tag": "
", - } + r.GET("/someJSON", func(c *gin.Context) { + data := gin.H{ + "lang": "GO语言", + "tag": "
", + } - // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"} - c.AsciiJSON(http.StatusOK, data) - }) + // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"} + c.AsciiJSON(http.StatusOK, data) + }) - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") } ``` @@ -1227,24 +1239,24 @@ This feature is unavailable in Go 1.6 and lower. ```go func main() { - r := gin.Default() + r := gin.Default() - // Serves unicode entities - r.GET("/json", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "html": "Hello, world!", - }) - }) + // Serves unicode entities + r.GET("/json", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "html": "Hello, world!", + }) + }) - // Serves literal characters - r.GET("/purejson", func(c *gin.Context) { - c.PureJSON(http.StatusOK, gin.H{ - "html": "Hello, world!", - }) - }) + // Serves literal characters + r.GET("/purejson", func(c *gin.Context) { + c.PureJSON(http.StatusOK, gin.H{ + "html": "Hello, world!", + }) + }) - // listen and serve on 0.0.0.0:8080 - r.Run(":8080") + // listen and serve on 0.0.0.0:8080 + r.Run(":8080") } ``` @@ -1252,14 +1264,14 @@ func main() { ```go func main() { - router := gin.Default() - router.Static("/assets", "./assets") - router.StaticFS("/more_static", http.Dir("my_file_system")) - router.StaticFile("/favicon.ico", "./resources/favicon.ico") - router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system")) - - // Listen and serve on 0.0.0.0:8080 - router.Run(":8080") + router := gin.Default() + router.Static("/assets", "./assets") + router.StaticFS("/more_static", http.Dir("my_file_system")) + router.StaticFile("/favicon.ico", "./resources/favicon.ico") + router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system")) + + // Listen and serve on 0.0.0.0:8080 + router.Run(":8080") } ``` @@ -1267,16 +1279,16 @@ func main() { ```go func main() { - router := gin.Default() + router := gin.Default() - router.GET("/local/file", func(c *gin.Context) { - c.File("local/file.go") - }) + router.GET("/local/file", func(c *gin.Context) { + c.File("local/file.go") + }) - var fs http.FileSystem = // ... - router.GET("/fs/file", func(c *gin.Context) { - c.FileFromFS("fs/file.go", fs) - }) + var fs http.FileSystem = // ... + router.GET("/fs/file", func(c *gin.Context) { + c.FileFromFS("fs/file.go", fs) + }) } ``` @@ -1285,26 +1297,26 @@ func main() { ```go func main() { - router := gin.Default() - router.GET("/someDataFromReader", func(c *gin.Context) { - response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png") - if err != nil || response.StatusCode != http.StatusOK { - c.Status(http.StatusServiceUnavailable) - return - } + router := gin.Default() + router.GET("/someDataFromReader", func(c *gin.Context) { + response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png") + if err != nil || response.StatusCode != http.StatusOK { + c.Status(http.StatusServiceUnavailable) + return + } - reader := response.Body - defer reader.Close() - contentLength := response.ContentLength - contentType := response.Header.Get("Content-Type") + reader := response.Body + defer reader.Close() + contentLength := response.ContentLength + contentType := response.Header.Get("Content-Type") - extraHeaders := map[string]string{ - "Content-Disposition": `attachment; filename="gopher.png"`, - } + extraHeaders := map[string]string{ + "Content-Disposition": `attachment; filename="gopher.png"`, + } - c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders) - }) - router.Run(":8080") + c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders) + }) + router.Run(":8080") } ``` @@ -1314,15 +1326,15 @@ Using LoadHTMLGlob() or LoadHTMLFiles() ```go func main() { - router := gin.Default() - router.LoadHTMLGlob("templates/*") - //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") - router.GET("/index", func(c *gin.Context) { - c.HTML(http.StatusOK, "index.tmpl", gin.H{ - "title": "Main website", - }) - }) - router.Run(":8080") + router := gin.Default() + router.LoadHTMLGlob("templates/*") + //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") + router.GET("/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "index.tmpl", gin.H{ + "title": "Main website", + }) + }) + router.Run(":8080") } ``` @@ -1330,9 +1342,9 @@ templates/index.tmpl ```html -

- {{ .title }} -

+

+ {{ .title }} +

``` @@ -1340,19 +1352,19 @@ Using templates with same name in different directories ```go func main() { - router := gin.Default() - router.LoadHTMLGlob("templates/**/*") - router.GET("/posts/index", func(c *gin.Context) { - c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ - "title": "Posts", - }) - }) - router.GET("/users/index", func(c *gin.Context) { - c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ - "title": "Users", - }) - }) - router.Run(":8080") + router := gin.Default() + router.LoadHTMLGlob("templates/**/*") + router.GET("/posts/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ + "title": "Posts", + }) + }) + router.GET("/users/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ + "title": "Users", + }) + }) + router.Run(":8080") } ``` @@ -1361,7 +1373,7 @@ templates/posts/index.tmpl ```html {{ define "posts/index.tmpl" }}

- {{ .title }} + {{ .title }}

Using posts/index.tmpl

@@ -1373,7 +1385,7 @@ templates/users/index.tmpl ```html {{ define "users/index.tmpl" }}

- {{ .title }} + {{ .title }}

Using users/index.tmpl

@@ -1388,10 +1400,10 @@ You can also use your own html template render import "html/template" func main() { - router := gin.Default() - html := template.Must(template.ParseFiles("file1", "file2")) - router.SetHTMLTemplate(html) - router.Run(":8080") + router := gin.Default() + html := template.Must(template.ParseFiles("file1", "file2")) + router.SetHTMLTemplate(html) + router.Run(":8080") } ``` @@ -1400,9 +1412,9 @@ func main() { You may use custom delims ```go - r := gin.Default() - r.Delims("{[{", "}]}") - r.LoadHTMLGlob("/path/to/templates") + r := gin.Default() + r.Delims("{[{", "}]}") + r.LoadHTMLGlob("/path/to/templates") ``` #### Custom Template Funcs @@ -1452,7 +1464,8 @@ Date: {[{.now | formatAsDate}]} ``` Result: -``` + +```sh Date: 2017/07/01 ``` @@ -1466,14 +1479,15 @@ Issuing a HTTP redirect is easy. Both internal and external locations are suppor ```go r.GET("/test", func(c *gin.Context) { - c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") + c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") }) ``` Issuing a HTTP redirect from POST. Refer to issue: [#444](https://github.com/gin-gonic/gin/issues/444) + ```go r.POST("/test", func(c *gin.Context) { - c.Redirect(http.StatusFound, "/foo") + c.Redirect(http.StatusFound, "/foo") }) ``` @@ -1489,44 +1503,43 @@ r.GET("/test2", func(c *gin.Context) { }) ``` - ### Custom Middleware ```go func Logger() gin.HandlerFunc { - return func(c *gin.Context) { - t := time.Now() + return func(c *gin.Context) { + t := time.Now() - // Set example variable - c.Set("example", "12345") + // Set example variable + c.Set("example", "12345") - // before request + // before request - c.Next() + c.Next() - // after request - latency := time.Since(t) - log.Print(latency) + // after request + latency := time.Since(t) + log.Print(latency) - // access the status we are sending - status := c.Writer.Status() - log.Println(status) - } + // access the status we are sending + status := c.Writer.Status() + log.Println(status) + } } func main() { - r := gin.New() - r.Use(Logger()) + r := gin.New() + r.Use(Logger()) - r.GET("/test", func(c *gin.Context) { - example := c.MustGet("example").(string) + r.GET("/test", func(c *gin.Context) { + example := c.MustGet("example").(string) - // it would print: "12345" - log.Println(example) - }) + // it would print: "12345" + log.Println(example) + }) - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") } ``` @@ -1535,37 +1548,37 @@ func main() { ```go // simulate some private data var secrets = gin.H{ - "foo": gin.H{"email": "foo@bar.com", "phone": "123433"}, - "austin": gin.H{"email": "austin@example.com", "phone": "666"}, - "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"}, + "foo": gin.H{"email": "foo@bar.com", "phone": "123433"}, + "austin": gin.H{"email": "austin@example.com", "phone": "666"}, + "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"}, } func main() { - r := gin.Default() - - // Group using gin.BasicAuth() middleware - // gin.Accounts is a shortcut for map[string]string - authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ - "foo": "bar", - "austin": "1234", - "lena": "hello2", - "manu": "4321", - })) - - // /admin/secrets endpoint - // hit "localhost:8080/admin/secrets - authorized.GET("/secrets", func(c *gin.Context) { - // get user, it was set by the BasicAuth middleware - user := c.MustGet(gin.AuthUserKey).(string) - if secret, ok := secrets[user]; ok { - c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) - } else { - c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("}) - } - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") + r := gin.Default() + + // Group using gin.BasicAuth() middleware + // gin.Accounts is a shortcut for map[string]string + authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ + "foo": "bar", + "austin": "1234", + "lena": "hello2", + "manu": "4321", + })) + + // /admin/secrets endpoint + // hit "localhost:8080/admin/secrets + authorized.GET("/secrets", func(c *gin.Context) { + // get user, it was set by the BasicAuth middleware + user := c.MustGet(gin.AuthUserKey).(string) + if secret, ok := secrets[user]; ok { + c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) + } else { + c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("}) + } + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") } ``` @@ -1575,30 +1588,30 @@ When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** ```go func main() { - r := gin.Default() + r := gin.Default() - r.GET("/long_async", func(c *gin.Context) { - // create copy to be used inside the goroutine - cCp := c.Copy() - go func() { - // simulate a long task with time.Sleep(). 5 seconds - time.Sleep(5 * time.Second) + r.GET("/long_async", func(c *gin.Context) { + // create copy to be used inside the goroutine + cCp := c.Copy() + go func() { + // simulate a long task with time.Sleep(). 5 seconds + time.Sleep(5 * time.Second) - // note that you are using the copied context "cCp", IMPORTANT - log.Println("Done! in path " + cCp.Request.URL.Path) - }() - }) + // note that you are using the copied context "cCp", IMPORTANT + log.Println("Done! in path " + cCp.Request.URL.Path) + }() + }) - r.GET("/long_sync", func(c *gin.Context) { - // simulate a long task with time.Sleep(). 5 seconds - time.Sleep(5 * time.Second) + r.GET("/long_sync", func(c *gin.Context) { + // simulate a long task with time.Sleep(). 5 seconds + time.Sleep(5 * time.Second) - // since we are NOT using a goroutine, we do not have to copy the context - log.Println("Done! in path " + c.Request.URL.Path) - }) + // since we are NOT using a goroutine, we do not have to copy the context + log.Println("Done! in path " + c.Request.URL.Path) + }) - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") } ``` @@ -1608,24 +1621,25 @@ Use `http.ListenAndServe()` directly, like this: ```go func main() { - router := gin.Default() - http.ListenAndServe(":8080", router) + router := gin.Default() + http.ListenAndServe(":8080", router) } ``` + or ```go func main() { - router := gin.Default() - - s := &http.Server{ - Addr: ":8080", - Handler: router, - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - MaxHeaderBytes: 1 << 20, - } - s.ListenAndServe() + router := gin.Default() + + s := &http.Server{ + Addr: ":8080", + Handler: router, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + s.ListenAndServe() } ``` @@ -1637,22 +1651,22 @@ example for 1-line LetsEncrypt HTTPS servers. package main import ( - "log" - "net/http" + "log" + "net/http" - "github.com/gin-gonic/autotls" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/autotls" + "github.com/gin-gonic/gin" ) func main() { - r := gin.Default() + r := gin.Default() - // Ping handler - r.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) + // Ping handler + r.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) - log.Fatal(autotls.Run(r, "example1.com", "example2.com")) + log.Fatal(autotls.Run(r, "example1.com", "example2.com")) } ``` @@ -1662,29 +1676,29 @@ example for custom autocert manager. package main import ( - "log" - "net/http" + "log" + "net/http" - "github.com/gin-gonic/autotls" - "github.com/gin-gonic/gin" - "golang.org/x/crypto/acme/autocert" + "github.com/gin-gonic/autotls" + "github.com/gin-gonic/gin" + "golang.org/x/crypto/acme/autocert" ) func main() { - r := gin.Default() + r := gin.Default() - // Ping handler - r.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) + // Ping handler + r.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) - m := autocert.Manager{ - Prompt: autocert.AcceptTOS, - HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), - Cache: autocert.DirCache("/var/www/.cache"), - } + m := autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), + Cache: autocert.DirCache("/var/www/.cache"), + } - log.Fatal(autotls.RunWithManager(r, &m)) + log.Fatal(autotls.RunWithManager(r, &m)) } ``` @@ -1696,84 +1710,84 @@ See the [question](https://github.com/gin-gonic/gin/issues/346) and try the foll package main import ( - "log" - "net/http" - "time" + "log" + "net/http" + "time" - "github.com/gin-gonic/gin" - "golang.org/x/sync/errgroup" + "github.com/gin-gonic/gin" + "golang.org/x/sync/errgroup" ) var ( - g errgroup.Group + g errgroup.Group ) func router01() http.Handler { - e := gin.New() - e.Use(gin.Recovery()) - e.GET("/", func(c *gin.Context) { - c.JSON( - http.StatusOK, - gin.H{ - "code": http.StatusOK, - "error": "Welcome server 01", - }, - ) - }) + e := gin.New() + e.Use(gin.Recovery()) + e.GET("/", func(c *gin.Context) { + c.JSON( + http.StatusOK, + gin.H{ + "code": http.StatusOK, + "error": "Welcome server 01", + }, + ) + }) - return e + return e } func router02() http.Handler { - e := gin.New() - e.Use(gin.Recovery()) - e.GET("/", func(c *gin.Context) { - c.JSON( - http.StatusOK, - gin.H{ - "code": http.StatusOK, - "error": "Welcome server 02", - }, - ) - }) + e := gin.New() + e.Use(gin.Recovery()) + e.GET("/", func(c *gin.Context) { + c.JSON( + http.StatusOK, + gin.H{ + "code": http.StatusOK, + "error": "Welcome server 02", + }, + ) + }) - return e + return e } func main() { - server01 := &http.Server{ - Addr: ":8080", - Handler: router01(), - ReadTimeout: 5 * time.Second, - WriteTimeout: 10 * time.Second, - } - - server02 := &http.Server{ - Addr: ":8081", - Handler: router02(), - ReadTimeout: 5 * time.Second, - WriteTimeout: 10 * time.Second, - } - - g.Go(func() error { - err := server01.ListenAndServe() - if err != nil && err != http.ErrServerClosed { - log.Fatal(err) - } - return err - }) - - g.Go(func() error { - err := server02.ListenAndServe() - if err != nil && err != http.ErrServerClosed { - log.Fatal(err) - } - return err - }) - - if err := g.Wait(); err != nil { - log.Fatal(err) - } + server01 := &http.Server{ + Addr: ":8080", + Handler: router01(), + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + server02 := &http.Server{ + Addr: ":8081", + Handler: router02(), + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + g.Go(func() error { + err := server01.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatal(err) + } + return err + }) + + g.Go(func() error { + err := server02.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatal(err) + } + return err + }) + + if err := g.Wait(); err != nil { + log.Fatal(err) + } } ``` @@ -1794,9 +1808,9 @@ endless.ListenAndServe(":4242", router) Alternatives: -* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. -* [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server. * [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy for Go servers. +* [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server. +* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. #### Manually @@ -1808,57 +1822,57 @@ In case you are using Go 1.8 or a later version, you may not need to use those l package main import ( - "context" - "log" - "net/http" - "os" - "os/signal" - "syscall" - "time" - - "github.com/gin-gonic/gin" + "context" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/gin-gonic/gin" ) func main() { - router := gin.Default() - router.GET("/", func(c *gin.Context) { - time.Sleep(5 * time.Second) - c.String(http.StatusOK, "Welcome Gin Server") - }) - - srv := &http.Server{ - Addr: ":8080", - Handler: router, - } - - // Initializing the server in a goroutine so that - // it won't block the graceful shutdown handling below - go func() { - if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { - log.Printf("listen: %s\n", err) - } - }() - - // Wait for interrupt signal to gracefully shutdown the server with - // a timeout of 5 seconds. - quit := make(chan os.Signal) - // kill (no param) default send syscall.SIGTERM - // kill -2 is syscall.SIGINT - // kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it - signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) - <-quit - log.Println("Shutting down server...") - - // The context is used to inform the server it has 5 seconds to finish - // the request it is currently handling - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - if err := srv.Shutdown(ctx); err != nil { - log.Fatal("Server forced to shutdown:", err) - } - - log.Println("Server exiting") + router := gin.Default() + router.GET("/", func(c *gin.Context) { + time.Sleep(5 * time.Second) + c.String(http.StatusOK, "Welcome Gin Server") + }) + + srv := &http.Server{ + Addr: ":8080", + Handler: router, + } + + // Initializing the server in a goroutine so that + // it won't block the graceful shutdown handling below + go func() { + if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { + log.Printf("listen: %s\n", err) + } + }() + + // Wait for interrupt signal to gracefully shutdown the server with + // a timeout of 5 seconds. + quit := make(chan os.Signal) + // kill (no param) default send syscall.SIGTERM + // kill -2 is syscall.SIGINT + // kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + log.Println("Shutting down server...") + + // The context is used to inform the server it has 5 seconds to finish + // the request it is currently handling + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := srv.Shutdown(ctx); err != nil { + log.Fatal("Server forced to shutdown:", err) + } + + log.Println("Server exiting") } ``` @@ -1870,38 +1884,38 @@ You can build a server into a single binary containing templates by using [go-as ```go func main() { - r := gin.New() + r := gin.New() - t, err := loadTemplate() - if err != nil { - panic(err) - } - r.SetHTMLTemplate(t) + t, err := loadTemplate() + if err != nil { + panic(err) + } + r.SetHTMLTemplate(t) - r.GET("/", func(c *gin.Context) { - c.HTML(http.StatusOK, "/html/index.tmpl",nil) - }) - r.Run(":8080") + r.GET("/", func(c *gin.Context) { + c.HTML(http.StatusOK, "/html/index.tmpl",nil) + }) + r.Run(":8080") } // loadTemplate loads templates embedded by go-assets-builder func loadTemplate() (*template.Template, error) { - t := template.New("") - for name, file := range Assets.Files { - defer file.Close() - if file.IsDir() || !strings.HasSuffix(name, ".tmpl") { - continue - } - h, err := ioutil.ReadAll(file) - if err != nil { - return nil, err - } - t, err = t.New(name).Parse(string(h)) - if err != nil { - return nil, err - } - } - return t, nil + t := template.New("") + for name, file := range Assets.Files { + defer file.Close() + if file.IsDir() || !strings.HasSuffix(name, ".tmpl") { + continue + } + h, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + t, err = t.New(name).Parse(string(h)) + if err != nil { + return nil, err + } + } + return t, nil } ``` @@ -1972,7 +1986,7 @@ func main() { Using the command `curl` command result: -``` +```sh $ curl "http://localhost:8080/getb?field_a=hello&field_b=world" {"a":{"FieldA":"hello"},"b":"world"} $ curl "http://localhost:8080/getc?field_a=hello&field_c=world" @@ -2031,10 +2045,10 @@ func SomeHandler(c *gin.Context) { } ``` -* `c.ShouldBindBodyWith` stores body into the context before binding. This has +1. `c.ShouldBindBodyWith` stores body into the context before binding. This has a slight impact to performance, so you should not use this method if you are enough to call binding at once. -* This feature is only needed for some formats -- `JSON`, `XML`, `MsgPack`, +2. This feature is only needed for some formats -- `JSON`, `XML`, `MsgPack`, `ProtoBuf`. For other formats, `Query`, `Form`, `FormPost`, `FormMultipart`, can be called by `c.ShouldBind()` multiple times without any damage to performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)). @@ -2043,54 +2057,54 @@ performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)). ```go const ( - customerTag = "url" - defaultMemory = 32 << 20 + customerTag = "url" + defaultMemory = 32 << 20 ) type customerBinding struct {} func (customerBinding) Name() string { - return "form" + return "form" } func (customerBinding) Bind(req *http.Request, obj interface{}) error { - if err := req.ParseForm(); err != nil { - return err - } - if err := req.ParseMultipartForm(defaultMemory); err != nil { - if err != http.ErrNotMultipart { - return err - } - } - if err := binding.MapFormWithTag(obj, req.Form, customerTag); err != nil { - return err - } - return validate(obj) + if err := req.ParseForm(); err != nil { + return err + } + if err := req.ParseMultipartForm(defaultMemory); err != nil { + if err != http.ErrNotMultipart { + return err + } + } + if err := binding.MapFormWithTag(obj, req.Form, customerTag); err != nil { + return err + } + return validate(obj) } func validate(obj interface{}) error { - if binding.Validator == nil { - return nil - } - return binding.Validator.ValidateStruct(obj) + if binding.Validator == nil { + return nil + } + return binding.Validator.ValidateStruct(obj) } // Now we can do this!!! // FormA is a external type that we can't modify it's tag type FormA struct { - FieldA string `url:"field_a"` + FieldA string `url:"field_a"` } func ListHandler(s *Service) func(ctx *gin.Context) { - return func(ctx *gin.Context) { - var urlBinding = customerBinding{} - var opt FormA - err := ctx.MustBindWith(&opt, urlBinding) - if err != nil { - ... - } - ... - } + return func(ctx *gin.Context) { + var urlBinding = customerBinding{} + var opt FormA + err := ctx.MustBindWith(&opt, urlBinding) + if err != nil { + ... + } + ... + } } ``` @@ -2102,11 +2116,11 @@ http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.gol package main import ( - "html/template" - "log" - "net/http" + "html/template" + "log" + "net/http" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) var html = template.Must(template.New("https").Parse(` @@ -2122,31 +2136,32 @@ var html = template.Must(template.New("https").Parse(` `)) func main() { - r := gin.Default() - r.Static("/assets", "./assets") - r.SetHTMLTemplate(html) - - r.GET("/", func(c *gin.Context) { - if pusher := c.Writer.Pusher(); pusher != nil { - // use pusher.Push() to do server push - if err := pusher.Push("/assets/app.js", nil); err != nil { - log.Printf("Failed to push: %v", err) - } - } - c.HTML(http.StatusOK, "https", gin.H{ - "status": "success", - }) - }) + r := gin.Default() + r.Static("/assets", "./assets") + r.SetHTMLTemplate(html) + + r.GET("/", func(c *gin.Context) { + if pusher := c.Writer.Pusher(); pusher != nil { + // use pusher.Push() to do server push + if err := pusher.Push("/assets/app.js", nil); err != nil { + log.Printf("Failed to push: %v", err) + } + } + c.HTML(http.StatusOK, "https", gin.H{ + "status": "success", + }) + }) - // Listen and Server in https://127.0.0.1:8080 - r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key") + // Listen and Server in https://127.0.0.1:8080 + r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key") } ``` ### Define format for the log of routes The default log of routes is: -``` + +```sh [GIN-debug] POST /foo --> main.main.func1 (3 handlers) [GIN-debug] GET /bar --> main.main.func2 (3 handlers) [GIN-debug] GET /status --> main.main.func3 (3 handlers) @@ -2154,34 +2169,35 @@ The default log of routes is: If you want to log this information in given format (e.g. JSON, key values or something else), then you can define this format with `gin.DebugPrintRouteFunc`. In the example below, we log all routes with standard log package but you can use another log tools that suits of your needs. + ```go import ( - "log" - "net/http" + "log" + "net/http" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) func main() { - r := gin.Default() - gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { - log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) - } + r := gin.Default() + gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { + log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) + } - r.POST("/foo", func(c *gin.Context) { - c.JSON(http.StatusOK, "foo") - }) + r.POST("/foo", func(c *gin.Context) { + c.JSON(http.StatusOK, "foo") + }) - r.GET("/bar", func(c *gin.Context) { - c.JSON(http.StatusOK, "bar") - }) + r.GET("/bar", func(c *gin.Context) { + c.JSON(http.StatusOK, "bar") + }) - r.GET("/status", func(c *gin.Context) { - c.JSON(http.StatusOK, "ok") - }) + r.GET("/status", func(c *gin.Context) { + c.JSON(http.StatusOK, "ok") + }) - // Listen and Server in http://0.0.0.0:8080 - r.Run() + // Listen and Server in http://0.0.0.0:8080 + r.Run() } ``` @@ -2233,52 +2249,53 @@ unnecessary computation. ```go import ( - "fmt" + "fmt" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) func main() { - router := gin.Default() - router.SetTrustedProxies([]string{"192.168.1.2"}) + router := gin.Default() + router.SetTrustedProxies([]string{"192.168.1.2"}) - router.GET("/", func(c *gin.Context) { - // If the client is 192.168.1.2, use the X-Forwarded-For - // header to deduce the original client IP from the trust- - // worthy parts of that header. - // Otherwise, simply return the direct client IP - fmt.Printf("ClientIP: %s\n", c.ClientIP()) - }) - router.Run() + router.GET("/", func(c *gin.Context) { + // If the client is 192.168.1.2, use the X-Forwarded-For + // header to deduce the original client IP from the trust- + // worthy parts of that header. + // Otherwise, simply return the direct client IP + fmt.Printf("ClientIP: %s\n", c.ClientIP()) + }) + router.Run() } ``` **Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform` to skip TrustedProxies check, it has a higher priority than TrustedProxies. Look at the example below: + ```go import ( - "fmt" + "fmt" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) func main() { - router := gin.Default() - // Use predefined header gin.PlatformXXX - router.TrustedPlatform = gin.PlatformGoogleAppEngine - // Or set your own trusted request header for another trusted proxy service - // Don't set it to any suspect request header, it's unsafe - router.TrustedPlatform = "X-CDN-IP" + router := gin.Default() + // Use predefined header gin.PlatformXXX + router.TrustedPlatform = gin.PlatformGoogleAppEngine + // Or set your own trusted request header for another trusted proxy service + // Don't set it to any suspect request header, it's unsafe + router.TrustedPlatform = "X-CDN-IP" - router.GET("/", func(c *gin.Context) { - // If you set TrustedPlatform, ClientIP() will resolve the - // corresponding header and return IP directly - fmt.Printf("ClientIP: %s\n", c.ClientIP()) - }) - router.Run() + router.GET("/", func(c *gin.Context) { + // If you set TrustedPlatform, ClientIP() will resolve the + // corresponding header and return IP directly + fmt.Printf("ClientIP: %s\n", c.ClientIP()) + }) + router.Run() } ``` @@ -2290,22 +2307,22 @@ The `net/http/httptest` package is preferable way for HTTP testing. package main import ( - "net/http" + "net/http" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) func setupRouter() *gin.Engine { - r := gin.Default() - r.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) - return r + r := gin.Default() + r.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) + return r } func main() { - r := setupRouter() - r.Run(":8080") + r := setupRouter() + r.Run(":8080") } ``` @@ -2315,22 +2332,22 @@ Test for code example above: package main import ( - "net/http" - "net/http/httptest" - "testing" + "net/http" + "net/http/httptest" + "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestPingRoute(t *testing.T) { - router := setupRouter() + router := setupRouter() - w := httptest.NewRecorder() - req, _ := http.NewRequest(http.MethodGet, "/ping", nil) - router.ServeHTTP(w, req) + w := httptest.NewRecorder() + req, _ := http.NewRequest(http.MethodGet, "/ping", nil) + router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Code) - assert.Equal(t, "pong", w.Body.String()) + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "pong", w.Body.String()) } ``` @@ -2345,4 +2362,3 @@ Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framewor * [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. * [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes. * [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. - From 8374ed2268e39c1033f6f3dc5c794b399285f164 Mon Sep 17 00:00:00 2001 From: Rainshaw Date: Tue, 2 Aug 2022 10:20:59 +0800 Subject: [PATCH 147/346] feat: add sonic json support (#3184) * feat: add sonic json support * fix: add blank line in readme --- .github/workflows/gin.yml | 2 +- Makefile | 2 +- README.md | 6 ++++++ go.mod | 5 +++++ go.sum | 23 +++++++++++++++++++++++ internal/json/json.go | 6 ++++-- internal/json/sonic.go | 27 +++++++++++++++++++++++++++ 7 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 internal/json/sonic.go diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 6dc787a2d0..cb3a49a831 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -29,7 +29,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest] go: [1.15, 1.16, 1.17, 1.18] - test-tags: ['', nomsgpack] + test-tags: ['', '-tags nomsgpack', '-tags "sonic avx"', '-tags go_json'] include: - os: ubuntu-latest go-build: ~/.cache/go-build diff --git a/Makefile b/Makefile index 5d55b444ce..ebde4ee840 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ TESTTAGS ?= "" test: echo "mode: count" > coverage.out for d in $(TESTFOLDER); do \ - $(GO) test -tags $(TESTTAGS) -v -covermode=count -coverprofile=profile.out $$d > tmp.out; \ + $(GO) test $(TESTTAGS) -v -covermode=count -coverprofile=profile.out $$d > tmp.out; \ cat tmp.out; \ if grep -q "^--- FAIL" tmp.out; then \ rm tmp.out; \ diff --git a/README.md b/README.md index 1c315f8869..8d7b5ec428 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,12 @@ go build -tags=jsoniter . go build -tags=go_json . ``` +[sonic](https://github.com/bytedance/sonic) (you have to ensure that your cpu support avx instruction.) + +```sh +$ go build -tags="sonic avx" . +``` + ## Build without `MsgPack` rendering feature Gin enables `MsgPack` rendering feature by default. But you can disable this feature by specifying `nomsgpack` build tag. diff --git a/go.mod b/go.mod index 0b259e132c..ed6402e30b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( + github.com/bytedance/sonic v1.3.2 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.10.0 github.com/goccy/go-json v0.9.10 @@ -17,13 +18,17 @@ require ( ) require ( + github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.0.14 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect golang.org/x/text v0.3.6 // indirect diff --git a/go.sum b/go.sum index 640ed10e4d..9b4fb91e46 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,8 @@ +github.com/bytedance/sonic v1.3.2 h1:xpJnWeCzu+XBfGBtNpk8jrMLZ+UduMEx0rAbHkFK5Cs= +github.com/bytedance/sonic v1.3.2/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a h1:lmGPzuocwDxoPAMr9h16zoJY/USZR9jIh99nrmKk1uI= +github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -20,6 +25,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.14 h1:QRqdp6bb9M9S5yyKeYteXKuoKE4p0tGlra81fKOpWH8= +github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= 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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= @@ -53,9 +61,23 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M= +github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc= +github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU= +golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 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/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= @@ -86,3 +108,4 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/json/json.go b/internal/json/json.go index a26d7db2e2..c5f3efc88d 100644 --- a/internal/json/json.go +++ b/internal/json/json.go @@ -2,8 +2,10 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. -//go:build !jsoniter && !go_json -// +build !jsoniter,!go_json +//go:build !jsoniter && !go_json && !(sonic && avx && (linux || windows || darwin) && amd64) +// +build !jsoniter +// +build !go_json +// +build !sonic !avx !linux,!windows,!darwin !amd64 package json diff --git a/internal/json/sonic.go b/internal/json/sonic.go new file mode 100644 index 0000000000..5a9ca4b2d0 --- /dev/null +++ b/internal/json/sonic.go @@ -0,0 +1,27 @@ +// Copyright 2022 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build sonic && avx && (linux || windows || darwin) && amd64 +// +build sonic +// +build avx +// +build linux windows darwin +// +build amd64 + +package json + +import "github.com/bytedance/sonic" + +var ( + json = sonic.ConfigStd + // Marshal is exported by gin/json package. + Marshal = json.Marshal + // Unmarshal is exported by gin/json package. + Unmarshal = json.Unmarshal + // MarshalIndent is exported by gin/json package. + MarshalIndent = json.MarshalIndent + // NewDecoder is exported by gin/json package. + NewDecoder = json.NewDecoder + // NewEncoder is exported by gin/json package. + NewEncoder = json.NewEncoder +) From ad66d9d11a7d79d08be897bc617cda1e20f71855 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:28:30 +0800 Subject: [PATCH 148/346] chore(deps): bump google.golang.org/protobuf from 1.28.0 to 1.28.1 (#3262) Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.28.0 to 1.28.1. - [Release notes](https://github.com/protocolbuffers/protobuf-go/releases) - [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash) - [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.28.0...v1.28.1) --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch ... 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 ed6402e30b..40f874ea3b 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/stretchr/testify v1.8.0 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 - google.golang.org/protobuf v1.28.0 + google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 9b4fb91e46..30dc0ce4f9 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 1b5ba251cfceec97020414b6c7dbc9fda697589a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 09:52:06 +0800 Subject: [PATCH 149/346] chore(deps): bump github.com/bytedance/sonic from 1.3.2 to 1.3.4 (#3273) Bumps [github.com/bytedance/sonic](https://github.com/bytedance/sonic) from 1.3.2 to 1.3.4. - [Release notes](https://github.com/bytedance/sonic/releases) - [Commits](https://github.com/bytedance/sonic/compare/v1.3.2...v1.3.4) --- updated-dependencies: - dependency-name: github.com/bytedance/sonic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 40f874ea3b..924cefc364 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( - github.com/bytedance/sonic v1.3.2 + github.com/bytedance/sonic v1.3.4 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.10.0 github.com/goccy/go-json v0.9.10 diff --git a/go.sum b/go.sum index 30dc0ce4f9..58be7b010c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/bytedance/sonic v1.3.2 h1:xpJnWeCzu+XBfGBtNpk8jrMLZ+UduMEx0rAbHkFK5Cs= -github.com/bytedance/sonic v1.3.2/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= +github.com/bytedance/sonic v1.3.4 h1:Pq+4YeIBh5VKMctAwqeiAsf18BCU24wZnwecwjIUCvU= +github.com/bytedance/sonic v1.3.4/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a h1:lmGPzuocwDxoPAMr9h16zoJY/USZR9jIh99nrmKk1uI= github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= @@ -17,6 +17,7 @@ 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.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc= github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= From b04917c53e310e3746ec90cd93106cda8c956211 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 15 Aug 2022 21:38:20 +0800 Subject: [PATCH 150/346] chore: upgrade golangci-lint and fix golangci-lint error (#3278) --- .github/workflows/gin.yml | 2 +- context.go | 70 ++++++++++++++++++++++----------------- context_test.go | 14 ++++---- errors.go | 9 ++--- errors_test.go | 6 ++-- ginS/gins.go | 3 +- logger_test.go | 6 ++-- middleware_test.go | 2 +- mode.go | 5 +-- path.go | 12 +++---- recovery.go | 2 +- routergroup.go | 3 +- 12 files changed, 74 insertions(+), 60 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index cb3a49a831..cff8f4a6dc 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@v3.2.0 with: - version: v1.45.0 + version: v1.48.0 args: --verbose test: needs: lint diff --git a/context.go b/context.go index 46bf113348..f9489a7734 100644 --- a/context.go +++ b/context.go @@ -153,9 +153,10 @@ func (c *Context) Handler() HandlerFunc { // FullPath returns a matched route full path. For not found routes // returns an empty string. -// router.GET("/user/:id", func(c *gin.Context) { -// c.FullPath() == "/user/:id" // true -// }) +// +// router.GET("/user/:id", func(c *gin.Context) { +// c.FullPath() == "/user/:id" // true +// }) func (c *Context) FullPath() string { return c.fullPath } @@ -382,10 +383,11 @@ func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) // Param returns the value of the URL param. // It is a shortcut for c.Params.ByName(key) -// router.GET("/user/:id", func(c *gin.Context) { -// // a GET request to /user/john -// id := c.Param("id") // id == "john" -// }) +// +// router.GET("/user/:id", func(c *gin.Context) { +// // a GET request to /user/john +// id := c.Param("id") // id == "john" +// }) func (c *Context) Param(key string) string { return c.Params.ByName(key) } @@ -402,11 +404,12 @@ func (c *Context) AddParam(key, value string) { // Query returns the keyed url query value if it exists, // otherwise it returns an empty string `("")`. // It is shortcut for `c.Request.URL.Query().Get(key)` -// GET /path?id=1234&name=Manu&value= -// c.Query("id") == "1234" -// c.Query("name") == "Manu" -// c.Query("value") == "" -// c.Query("wtf") == "" +// +// GET /path?id=1234&name=Manu&value= +// c.Query("id") == "1234" +// c.Query("name") == "Manu" +// c.Query("value") == "" +// c.Query("wtf") == "" func (c *Context) Query(key string) (value string) { value, _ = c.GetQuery(key) return @@ -415,10 +418,11 @@ func (c *Context) Query(key string) (value string) { // DefaultQuery returns the keyed url query value if it exists, // otherwise it returns the specified defaultValue string. // See: Query() and GetQuery() for further information. -// GET /?name=Manu&lastname= -// c.DefaultQuery("name", "unknown") == "Manu" -// c.DefaultQuery("id", "none") == "none" -// c.DefaultQuery("lastname", "none") == "" +// +// GET /?name=Manu&lastname= +// c.DefaultQuery("name", "unknown") == "Manu" +// c.DefaultQuery("id", "none") == "none" +// c.DefaultQuery("lastname", "none") == "" func (c *Context) DefaultQuery(key, defaultValue string) string { if value, ok := c.GetQuery(key); ok { return value @@ -430,10 +434,11 @@ func (c *Context) DefaultQuery(key, defaultValue string) string { // if it exists `(value, true)` (even when the value is an empty string), // otherwise it returns `("", false)`. // It is shortcut for `c.Request.URL.Query().Get(key)` -// GET /?name=Manu&lastname= -// ("Manu", true) == c.GetQuery("name") -// ("", false) == c.GetQuery("id") -// ("", true) == c.GetQuery("lastname") +// +// GET /?name=Manu&lastname= +// ("Manu", true) == c.GetQuery("name") +// ("", false) == c.GetQuery("id") +// ("", true) == c.GetQuery("lastname") func (c *Context) GetQuery(key string) (string, bool) { if values, ok := c.GetQueryArray(key); ok { return values[0], ok @@ -500,9 +505,10 @@ func (c *Context) DefaultPostForm(key, defaultValue string) string { // form or multipart form when it exists `(value, true)` (even when the value is an empty string), // otherwise it returns ("", false). // For example, during a PATCH request to update the user's email: -// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com" -// email= --> ("", true) := GetPostForm("email") // set email to "" -// --> ("", false) := GetPostForm("email") // do nothing with email +// +// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com" +// email= --> ("", true) := GetPostForm("email") // set email to "" +// --> ("", false) := GetPostForm("email") // do nothing with email func (c *Context) GetPostForm(key string) (string, bool) { if values, ok := c.GetPostFormArray(key); ok { return values[0], ok @@ -607,8 +613,10 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error // Bind checks the Method and Content-Type to select a binding engine automatically, // Depending on the "Content-Type" header different bindings are used, for example: -// "application/json" --> JSON binding -// "application/xml" --> XML binding +// +// "application/json" --> JSON binding +// "application/xml" --> XML binding +// // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It decodes the json payload into the struct specified as a pointer. // It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid. @@ -651,7 +659,7 @@ func (c *Context) BindHeader(obj any) error { // It will abort the request with HTTP 400 if any error occurs. func (c *Context) BindUri(obj any) error { if err := c.ShouldBindUri(obj); err != nil { - c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck + c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck return err } return nil @@ -662,7 +670,7 @@ func (c *Context) BindUri(obj any) error { // See the binding package. func (c *Context) MustBindWith(obj any, b binding.Binding) error { if err := c.ShouldBindWith(obj, b); err != nil { - c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck + c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck return err } return nil @@ -670,8 +678,10 @@ func (c *Context) MustBindWith(obj any, b binding.Binding) error { // ShouldBind checks the Method and Content-Type to select a binding engine automatically, // Depending on the "Content-Type" header different bindings are used, for example: -// "application/json" --> JSON binding -// "application/xml" --> XML binding +// +// "application/json" --> JSON binding +// "application/xml" --> XML binding +// // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It decodes the json payload into the struct specified as a pointer. // Like c.Bind() but this method does not set the response status code to 400 or abort if input is not valid. @@ -1112,7 +1122,7 @@ func (c *Context) Negotiate(code int, config Negotiate) { c.TOML(code, data) default: - c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck + c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) //nolint: errcheck } } diff --git a/context_test.go b/context_test.go index d09b0ae1ea..b3e81c1482 100644 --- a/context_test.go +++ b/context_test.go @@ -152,7 +152,7 @@ func TestContextReset(t *testing.T) { c.index = 2 c.Writer = &responseWriter{ResponseWriter: httptest.NewRecorder()} c.Params = Params{Param{}} - c.Error(errors.New("test")) // nolint: errcheck + c.Error(errors.New("test")) //nolint: errcheck c.Set("foo", "bar") c.reset() @@ -1376,12 +1376,12 @@ func TestContextError(t *testing.T) { assert.Empty(t, c.Errors) firstErr := errors.New("first error") - c.Error(firstErr) // nolint: errcheck + c.Error(firstErr) //nolint: errcheck assert.Len(t, c.Errors, 1) assert.Equal(t, "Error #01: first error\n", c.Errors.String()) secondErr := errors.New("second error") - c.Error(&Error{ // nolint: errcheck + c.Error(&Error{ //nolint: errcheck Err: secondErr, Meta: "some data 2", Type: ErrorTypePublic, @@ -1403,13 +1403,13 @@ func TestContextError(t *testing.T) { t.Error("didn't panic") } }() - c.Error(nil) // nolint: errcheck + c.Error(nil) //nolint: errcheck } func TestContextTypedError(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Error(errors.New("externo 0")).SetType(ErrorTypePublic) // nolint: errcheck - c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate) // nolint: errcheck + c.Error(errors.New("externo 0")).SetType(ErrorTypePublic) //nolint: errcheck + c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate) //nolint: errcheck for _, err := range c.Errors.ByType(ErrorTypePublic) { assert.Equal(t, ErrorTypePublic, err.Type) @@ -1424,7 +1424,7 @@ func TestContextAbortWithError(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.AbortWithError(http.StatusUnauthorized, errors.New("bad input")).SetMeta("some input") // nolint: errcheck + c.AbortWithError(http.StatusUnauthorized, errors.New("bad input")).SetMeta("some input") //nolint: errcheck assert.Equal(t, http.StatusUnauthorized, w.Code) assert.Equal(t, abortIndex, c.index) diff --git a/errors.go b/errors.go index 2853ce8ed8..ca2bfc3fdc 100644 --- a/errors.go +++ b/errors.go @@ -124,10 +124,11 @@ func (a errorMsgs) Last() *Error { // Errors returns an array with all the error messages. // Example: -// c.Error(errors.New("first")) -// c.Error(errors.New("second")) -// c.Error(errors.New("third")) -// c.Errors.Errors() // == []string{"first", "second", "third"} +// +// c.Error(errors.New("first")) +// c.Error(errors.New("second")) +// c.Error(errors.New("third")) +// c.Errors.Errors() // == []string{"first", "second", "third"} func (a errorMsgs) Errors() []string { if len(a) == 0 { return nil diff --git a/errors_test.go b/errors_test.go index 78d561c63b..f77a634296 100644 --- a/errors_test.go +++ b/errors_test.go @@ -35,7 +35,7 @@ func TestError(t *testing.T) { jsonBytes, _ := json.Marshal(err) assert.Equal(t, "{\"error\":\"test error\",\"meta\":\"some data\"}", string(jsonBytes)) - err.SetMeta(H{ // nolint: errcheck + err.SetMeta(H{ //nolint: errcheck "status": "200", "data": "some data", }) @@ -45,7 +45,7 @@ func TestError(t *testing.T) { "data": "some data", }, err.JSON()) - err.SetMeta(H{ // nolint: errcheck + err.SetMeta(H{ //nolint: errcheck "error": "custom error", "status": "200", "data": "some data", @@ -60,7 +60,7 @@ func TestError(t *testing.T) { status string data string } - err.SetMeta(customError{status: "200", data: "other data"}) // nolint: errcheck + err.SetMeta(customError{status: "200", data: "other data"}) //nolint: errcheck assert.Equal(t, customError{status: "200", data: "other data"}, err.JSON()) } diff --git a/ginS/gins.go b/ginS/gins.go index 1550b868d1..ea38c613ce 100644 --- a/ginS/gins.go +++ b/ginS/gins.go @@ -108,7 +108,8 @@ func StaticFile(relativePath, filepath string) gin.IRoutes { // of the Router's NotFound handler. // To use the operating system's file system implementation, // use : -// router.Static("/static", "/var/www") +// +// router.Static("/static", "/var/www") func Static(relativePath, root string) gin.IRoutes { return engine().Static(relativePath, root) } diff --git a/logger_test.go b/logger_test.go index fa0d9ce8be..7bc1137109 100644 --- a/logger_test.go +++ b/logger_test.go @@ -358,13 +358,13 @@ func TestErrorLogger(t *testing.T) { router := New() router.Use(ErrorLogger()) router.GET("/error", func(c *Context) { - c.Error(errors.New("this is an error")) // nolint: errcheck + c.Error(errors.New("this is an error")) //nolint: errcheck }) router.GET("/abort", func(c *Context) { - c.AbortWithError(http.StatusUnauthorized, errors.New("no authorized")) // nolint: errcheck + c.AbortWithError(http.StatusUnauthorized, errors.New("no authorized")) //nolint: errcheck }) router.GET("/print", func(c *Context) { - c.Error(errors.New("this is an error")) // nolint: errcheck + c.Error(errors.New("this is an error")) //nolint: errcheck c.String(http.StatusInternalServerError, "hola!") }) diff --git a/middleware_test.go b/middleware_test.go index a235fe9192..acdf89c42e 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -211,7 +211,7 @@ func TestMiddlewareFailHandlersChain(t *testing.T) { router := New() router.Use(func(context *Context) { signature += "A" - context.AbortWithError(http.StatusInternalServerError, errors.New("foo")) // nolint: errcheck + context.AbortWithError(http.StatusInternalServerError, errors.New("foo")) //nolint: errcheck }) router.Use(func(context *Context) { signature += "B" diff --git a/mode.go b/mode.go index 545fdaaf8f..fd26d907cc 100644 --- a/mode.go +++ b/mode.go @@ -35,8 +35,9 @@ const ( // Note that both Logger and Recovery provides custom ways to configure their // output io.Writer. // To support coloring in Windows use: -// import "github.com/mattn/go-colorable" -// gin.DefaultWriter = colorable.NewColorableStdout() +// +// import "github.com/mattn/go-colorable" +// gin.DefaultWriter = colorable.NewColorableStdout() var DefaultWriter io.Writer = os.Stdout // DefaultErrorWriter is the default io.Writer used by Gin to debug errors diff --git a/path.go b/path.go index d42d6b9d05..82438c1378 100644 --- a/path.go +++ b/path.go @@ -10,12 +10,12 @@ package gin // // The following rules are applied iteratively until no further processing can // be done: -// 1. Replace multiple slashes with a single slash. -// 2. Eliminate each . path name element (the current directory). -// 3. Eliminate each inner .. path name element (the parent directory) -// along with the non-.. element that precedes it. -// 4. Eliminate .. elements that begin a rooted path: -// that is, replace "/.." by "/" at the beginning of a path. +// 1. Replace multiple slashes with a single slash. +// 2. Eliminate each . path name element (the current directory). +// 3. Eliminate each inner .. path name element (the parent directory) +// along with the non-.. element that precedes it. +// 4. Eliminate .. elements that begin a rooted path: +// that is, replace "/.." by "/" at the beginning of a path. // // If the result of this process is an empty string, "/" is returned. func cleanPath(p string) string { diff --git a/recovery.go b/recovery.go index abb645105a..05b30d954d 100644 --- a/recovery.go +++ b/recovery.go @@ -91,7 +91,7 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc { } if brokenPipe { // If the connection is dead, we can't write a status to it. - c.Error(err.(error)) // nolint: errcheck + c.Error(err.(error)) //nolint: errcheck c.Abort() } else { handle(c, err) diff --git a/routergroup.go b/routergroup.go index 3c082d932b..2474a81c15 100644 --- a/routergroup.go +++ b/routergroup.go @@ -182,7 +182,8 @@ func (group *RouterGroup) staticFileHandler(relativePath string, handler Handler // of the Router's NotFound handler. // To use the operating system's file system implementation, // use : -// router.Static("/static", "/var/www") +// +// router.Static("/static", "/var/www") func (group *RouterGroup) Static(relativePath, root string) IRoutes { return group.StaticFS(relativePath, Dir(root, false)) } From de17fb1a33743a587641d39bb6c79a5e14673da7 Mon Sep 17 00:00:00 2001 From: Aoang Date: Wed, 17 Aug 2022 07:14:19 +0800 Subject: [PATCH 151/346] Format with Go 1.19 formatter (#3277) * Format with Go 1.19 formatter This allows the GoDoc to take advantage of new markup syntax introduced in Go 1.19. This does not require that our minimum supported version be bumped to Go 1.19 since the pkgsite renders our godoc regardless of supported Go version. * Add Format check --- .github/workflows/gin.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index cff8f4a6dc..14de5f7288 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -68,6 +68,10 @@ jobs: uses: codecov/codecov-action@v3 with: flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }} + + - name: Format + if: matrix.go-version == '1.19.x' + run: diff -u <(echo -n) <(gofmt -d .) notification-gitter: needs: test runs-on: ubuntu-latest From 1c48977cca9e7a0a41c763376b6921a23cd06fe2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 07:14:59 +0800 Subject: [PATCH 152/346] chore(deps): bump github.com/mattn/go-isatty from 0.0.14 to 0.0.16 (#3281) Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.14 to 0.0.16. - [Release notes](https://github.com/mattn/go-isatty/releases) - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.14...v0.0.16) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 924cefc364..54cc55230c 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/go-playground/validator/v10 v10.10.0 github.com/goccy/go-json v0.9.10 github.com/json-iterator/go v1.1.12 - github.com/mattn/go-isatty v0.0.14 + github.com/mattn/go-isatty v0.0.16 github.com/pelletier/go-toml/v2 v2.0.2 github.com/stretchr/testify v1.8.0 github.com/ugorji/go/codec v1.2.7 @@ -30,7 +30,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect - golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect golang.org/x/text v0.3.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 58be7b010c..b88e4a820f 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 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/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= @@ -85,9 +85,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxW golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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-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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= From fb13e822a4b48b872dd4538dd5bbd7a70767ca7a Mon Sep 17 00:00:00 2001 From: Alex <93376818+sashashura@users.noreply.github.com> Date: Wed, 31 Aug 2022 07:33:25 +0100 Subject: [PATCH 153/346] Update gin.yml (#3304) Signed-off-by: sashashura <93376818+sashashura@users.noreply.github.com> Signed-off-by: sashashura <93376818+sashashura@users.noreply.github.com> --- .github/workflows/gin.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 14de5f7288..004f9b9df0 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -8,6 +8,9 @@ on: branches: - master +permissions: + contents: read + jobs: lint: runs-on: ubuntu-latest From 2ae61570499d8bb5eb05e46d22a3754cf2635e63 Mon Sep 17 00:00:00 2001 From: Dave Rolsky Date: Wed, 31 Aug 2022 01:34:33 -0500 Subject: [PATCH 154/346] Fix typos in RouterGroup method docs (#3302) There were a number of spots referring to a variable named "handle" that should be "handlers". --- routergroup.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/routergroup.go b/routergroup.go index 2474a81c15..8b877edacb 100644 --- a/routergroup.go +++ b/routergroup.go @@ -106,37 +106,37 @@ func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...Ha return group.handle(httpMethod, relativePath, handlers) } -// POST is a shortcut for router.Handle("POST", path, handle). +// POST is a shortcut for router.Handle("POST", path, handlers). func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodPost, relativePath, handlers) } -// GET is a shortcut for router.Handle("GET", path, handle). +// GET is a shortcut for router.Handle("GET", path, handlers). func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodGet, relativePath, handlers) } -// DELETE is a shortcut for router.Handle("DELETE", path, handle). +// DELETE is a shortcut for router.Handle("DELETE", path, handlers). func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodDelete, relativePath, handlers) } -// PATCH is a shortcut for router.Handle("PATCH", path, handle). +// PATCH is a shortcut for router.Handle("PATCH", path, handlers). func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodPatch, relativePath, handlers) } -// PUT is a shortcut for router.Handle("PUT", path, handle). +// PUT is a shortcut for router.Handle("PUT", path, handlers). func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodPut, relativePath, handlers) } -// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle). +// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handlers). func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodOptions, relativePath, handlers) } -// HEAD is a shortcut for router.Handle("HEAD", path, handle). +// HEAD is a shortcut for router.Handle("HEAD", path, handlers). func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodHead, relativePath, handlers) } From de1f142ed4b3279a24f2d1ab7993c6ae1a6c292a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 10:01:58 +0800 Subject: [PATCH 155/346] chore(deps): bump github.com/goccy/go-json from 0.9.10 to 0.9.11 (#3292) Bumps [github.com/goccy/go-json](https://github.com/goccy/go-json) from 0.9.10 to 0.9.11. - [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.9.10...v0.9.11) --- updated-dependencies: - dependency-name: github.com/goccy/go-json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] 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 54cc55230c..885ff0ee41 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/bytedance/sonic v1.3.4 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.10.0 - github.com/goccy/go-json v0.9.10 + github.com/goccy/go-json v0.9.11 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.16 github.com/pelletier/go-toml/v2 v2.0.2 diff --git a/go.sum b/go.sum index b88e4a820f..8b560189a9 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc= -github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/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 0128d74f340ed31065125a5ee6a481f2965c366d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 10:02:40 +0800 Subject: [PATCH 156/346] chore(deps): bump github.com/bytedance/sonic from 1.3.4 to 1.4.0 (#3293) Bumps [github.com/bytedance/sonic](https://github.com/bytedance/sonic) from 1.3.4 to 1.4.0. - [Release notes](https://github.com/bytedance/sonic/releases) - [Commits](https://github.com/bytedance/sonic/compare/v1.3.4...v1.4.0) --- updated-dependencies: - dependency-name: github.com/bytedance/sonic dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] 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 885ff0ee41..daf52e2a42 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( - github.com/bytedance/sonic v1.3.4 + github.com/bytedance/sonic v1.4.0 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.10.0 github.com/goccy/go-json v0.9.11 diff --git a/go.sum b/go.sum index 8b560189a9..4d83273133 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/bytedance/sonic v1.3.4 h1:Pq+4YeIBh5VKMctAwqeiAsf18BCU24wZnwecwjIUCvU= -github.com/bytedance/sonic v1.3.4/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= +github.com/bytedance/sonic v1.4.0 h1:d6vgPhwgHfpmEiz/9Fzea9fGzWY7RO1TQEySBiRwDLY= +github.com/bytedance/sonic v1.4.0/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a h1:lmGPzuocwDxoPAMr9h16zoJY/USZR9jIh99nrmKk1uI= github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= From 2c9e5fe47ae55defc55dae30a0e63192e4538bc2 Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Thu, 1 Sep 2022 06:51:27 +0430 Subject: [PATCH 157/346] rename variable because collide with the imported package name (#3298) * rename variable because collide with the imported package name * handle unhandled error in context_1.17_test.go --- context_1.17_test.go | 7 ++++++- gin_test.go | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/context_1.17_test.go b/context_1.17_test.go index 69c97864ce..23377ffdd3 100644 --- a/context_1.17_test.go +++ b/context_1.17_test.go @@ -30,7 +30,12 @@ func (i interceptedWriter) WriteHeader(code int) { func TestContextFormFileFailed17(t *testing.T) { buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) - mw.Close() + defer func(mw *multipart.Writer) { + err := mw.Close() + if err != nil { + assert.Error(t, err) + } + }(mw) c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) diff --git a/gin_test.go b/gin_test.go index 02f23247d5..4fac677ac6 100644 --- a/gin_test.go +++ b/gin_test.go @@ -100,7 +100,7 @@ func TestH2c(t *testing.T) { url := "http://" + ln.Addr().String() + "/" - http := http.Client{ + httpClient := http.Client{ Transport: &http2.Transport{ AllowHTTP: true, DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) { @@ -109,7 +109,7 @@ func TestH2c(t *testing.T) { }, } - res, err := http.Get(url) + res, err := httpClient.Get(url) if err != nil { fmt.Println(err) } From 814cd188eb0b0304dd3c37b698edc51ff46e24a2 Mon Sep 17 00:00:00 2001 From: Konstantin Runov <101004736+runebone@users.noreply.github.com> Date: Sun, 18 Sep 2022 16:59:57 +0300 Subject: [PATCH 158/346] FIX TYPO: Gin by default useR -> ... useS (#3324) --- routergroup.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routergroup.go b/routergroup.go index 8b877edacb..27308bc8fd 100644 --- a/routergroup.go +++ b/routergroup.go @@ -161,7 +161,7 @@ func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes { // StaticFileFS works just like `StaticFile` but a custom `http.FileSystem` can be used instead.. // router.StaticFileFS("favicon.ico", "./resources/favicon.ico", Dir{".", false}) -// Gin by default user: gin.Dir() +// Gin by default uses: gin.Dir() func (group *RouterGroup) StaticFileFS(relativePath, filepath string, fs http.FileSystem) IRoutes { return group.staticFileHandler(relativePath, func(c *Context) { c.FileFromFS(filepath, fs) @@ -189,7 +189,7 @@ func (group *RouterGroup) Static(relativePath, root string) IRoutes { } // StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead. -// Gin by default user: gin.Dir() +// Gin by default uses: gin.Dir() func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes { if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") { panic("URL parameters can not be used when serving a static folder") From 78dad9d77d8c2d679dedea1fbef5fc8a54372efd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 14:44:55 +0800 Subject: [PATCH 159/346] chore(deps): bump github.com/go-playground/validator/v10 (#3330) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.10.0 to 10.11.1. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.10.0...v10.11.1) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 17 +++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index daf52e2a42..fa1db99ac4 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,14 @@ go 1.18 require ( github.com/bytedance/sonic v1.4.0 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.10.0 + github.com/go-playground/validator/v10 v10.11.1 github.com/goccy/go-json v0.9.11 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.16 github.com/pelletier/go-toml/v2 v2.0.2 github.com/stretchr/testify v1.8.0 github.com/ugorji/go/codec v1.2.7 - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -29,8 +29,8 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect - golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/text v0.3.7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4d83273133..1c31d29957 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= @@ -79,19 +79,20 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU= golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -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/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 6fab4c373eaabe3425b08fe1a5f1484c93d07c2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Oct 2022 15:39:38 +0800 Subject: [PATCH 160/346] chore(deps): bump actions/setup-go from 2 to 3 (#3340) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 64ed8b2b6e..654585b91d 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -19,7 +19,7 @@ jobs: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: 1.17 - From 6296175f70e21bd1c09efc424a2c14574904720d Mon Sep 17 00:00:00 2001 From: mstmdev Date: Wed, 12 Oct 2022 14:18:12 +0800 Subject: [PATCH 161/346] Fix the GO-2022-0969 and GO-2022-0288 vulnerabilities (#3333) --- .github/workflows/gin.yml | 2 +- go.mod | 2 +- go.sum | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 004f9b9df0..686d08a3ac 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -31,7 +31,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: [1.15, 1.16, 1.17, 1.18] + go: [1.16, 1.17, 1.18] test-tags: ['', '-tags nomsgpack', '-tags "sonic avx"', '-tags go_json'] include: - os: ubuntu-latest diff --git a/go.mod b/go.mod index fa1db99ac4..1c64cf1b15 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.2 github.com/stretchr/testify v1.8.0 github.com/ugorji/go/codec v1.2.7 - golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 + golang.org/x/net v0.0.0-20221004154528-8021a29435af google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 1c31d29957..35bfd2e9a8 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,9 @@ golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SX golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= +golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From fa58bff301a823ce25af800614d3f016b10ae586 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Sun, 16 Oct 2022 09:32:28 +0800 Subject: [PATCH 162/346] chore(dep): Changes minimum support go version to go1.16 (#3361) --- README.md | 2 +- debug.go | 4 ++-- debug_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8d7b5ec428..960c66dc1e 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi To install Gin package, you need to install Go and set your Go workspace first. -1. You first need [Go](https://golang.org/) installed (**version 1.15+ is required**), then you can use the below Go command to install Gin. +1. You first need [Go](https://golang.org/) installed (**version 1.16+ is required**), then you can use the below Go command to install Gin. ```sh go get -u github.com/gin-gonic/gin diff --git a/debug.go b/debug.go index b9f8234af3..cbcedbc98f 100644 --- a/debug.go +++ b/debug.go @@ -12,7 +12,7 @@ import ( "strings" ) -const ginSupportMinGoVer = 15 +const ginSupportMinGoVer = 16 // IsDebugging returns true if the framework is running in debug mode. // Use SetMode(gin.ReleaseMode) to disable debug mode. @@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) { func debugPrintWARNINGDefault() { if v, e := getMinVer(runtime.Version()); e == nil && v < ginSupportMinGoVer { - debugPrint(`[WARNING] Now Gin requires Go 1.15+. + debugPrint(`[WARNING] Now Gin requires Go 1.16+. `) } diff --git a/debug_test.go b/debug_test.go index bf0e6ab847..abe8b41cad 100644 --- a/debug_test.go +++ b/debug_test.go @@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { }) m, e := getMinVer(runtime.Version()) if e == nil && m < ginSupportMinGoVer { - assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.15+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) + assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.16+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } else { assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } From 4c64f1c3859fa853659ff1db65a8f0fa67856ed9 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Sun, 16 Oct 2022 09:33:26 +0800 Subject: [PATCH 163/346] =?UTF-8?q?chore(go):=20Add=C2=A0support=20go=201.?= =?UTF-8?q?19=20(#3272)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gin.yml | 2 +- context_1.17_test.go | 17 +++++++++++++++++ context_1.19_test.go | 31 +++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 context_1.19_test.go diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 686d08a3ac..18b36d0eb1 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -31,7 +31,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: [1.16, 1.17, 1.18] + go: [1.16, 1.17, 1.18, 1.19] test-tags: ['', '-tags nomsgpack', '-tags "sonic avx"', '-tags go_json'] include: - os: ubuntu-latest diff --git a/context_1.17_test.go b/context_1.17_test.go index 23377ffdd3..0f8527fe09 100644 --- a/context_1.17_test.go +++ b/context_1.17_test.go @@ -12,6 +12,8 @@ import ( "mime/multipart" "net/http" "net/http/httptest" + "runtime" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -28,6 +30,9 @@ func (i interceptedWriter) WriteHeader(code int) { } func TestContextFormFileFailed17(t *testing.T) { + if !isGo117OrGo118() { + return + } buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) defer func(mw *multipart.Writer) { @@ -75,3 +80,15 @@ func TestInterceptedHeader(t *testing.T) { assert.Equal(t, "", w.Result().Header.Get("X-Test")) assert.Equal(t, "present", w.Result().Header.Get("X-Test-2")) } + +func isGo117OrGo118() bool { + version := strings.Split(runtime.Version()[2:], ".") + if len(version) >= 2 { + x := version[0] + y := version[1] + if x == "1" && (y == "17" || y == "18") { + return true + } + } + return false +} diff --git a/context_1.19_test.go b/context_1.19_test.go new file mode 100644 index 0000000000..4b34ea2472 --- /dev/null +++ b/context_1.19_test.go @@ -0,0 +1,31 @@ +// Copyright 2022 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build go1.19 +// +build go1.19 + +package gin + +import ( + "bytes" + "mime/multipart" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestContextFormFileFailed19(t *testing.T) { + buf := new(bytes.Buffer) + mw := multipart.NewWriter(buf) + mw.Close() + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request.Header.Set("Content-Type", mw.FormDataContentType()) + c.engine.MaxMultipartMemory = 8 << 20 + f, err := c.FormFile("file") + assert.Error(t, err) + assert.Nil(t, f) +} From 24a1d2adb9dba64d38426d76f2a4cf76123a4711 Mon Sep 17 00:00:00 2001 From: John Bampton Date: Sun, 16 Oct 2022 11:41:14 +1000 Subject: [PATCH 164/346] fix(typo): spelling `covert` -> `convert` (#3325) --- binding/form_mapping.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 98cebfecf7..540bbbb84a 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -19,7 +19,7 @@ import ( var ( errUnknownType = errors.New("unknown type") - // ErrConvertMapStringSlice can not covert to map[string][]string + // ErrConvertMapStringSlice can not convert to map[string][]string ErrConvertMapStringSlice = errors.New("can not convert to map slices of strings") // ErrConvertToMapString can not convert to map[string]string From 45c758e2f9d36ee6a6e7d8b2131474970e41b12d Mon Sep 17 00:00:00 2001 From: Mohana sai krishna Kandula <73701479+mskKandula@users.noreply.github.com> Date: Sun, 16 Oct 2022 07:15:08 +0530 Subject: [PATCH 165/346] chore(file): Creates a directory named path (#3316) Co-authored-by: mohanak --- context.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/context.go b/context.go index f9489a7734..b66b8adc15 100644 --- a/context.go +++ b/context.go @@ -15,6 +15,7 @@ import ( "net/http" "net/url" "os" + "path/filepath" "strings" "sync" "time" @@ -601,6 +602,10 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error } defer src.Close() + if err = os.MkdirAll(filepath.Dir(dst), 0750); err != nil { + return err + } + out, err := os.Create(dst) if err != nil { return err From 33ab0fc155e68acbc7fd30281c0d9b2e6e091b7b Mon Sep 17 00:00:00 2001 From: hopehook Date: Sun, 16 Oct 2022 09:49:24 +0800 Subject: [PATCH 166/346] refactor(struct): Remove redundant type conversions (#3345) --- binding/binding_test.go | 8 ++------ binding/form_mapping_test.go | 8 ++++---- render/render_test.go | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/binding/binding_test.go b/binding/binding_test.go index f09962164d..eae278022f 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -1107,9 +1107,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s assert.Equal(t, struct { Idx int "form:\"idx\"" - }(struct { - Idx int "form:\"idx\"" - }{Idx: 123}), + }{Idx: 123}, obj.StructFoo) case "StructPointer": obj := FooStructForStructPointerType{} @@ -1118,9 +1116,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s assert.Equal(t, struct { Name string "form:\"name\"" - }(struct { - Name string "form:\"name\"" - }{Name: "thinkerou"}), + }{Name: "thinkerou"}, *obj.StructPointerFoo) case "Map": obj := FooStructForMapType{} diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 78f4df0e44..93d6a92ff8 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -114,7 +114,7 @@ func TestMappingPrivateField(t *testing.T) { } err := mappingByPtr(&s, formSource{"field": {"6"}}, "form") assert.NoError(t, err) - assert.Equal(t, int(0), s.f) + assert.Equal(t, 0, s.f) } func TestMappingUnknownFieldType(t *testing.T) { @@ -133,7 +133,7 @@ func TestMappingURI(t *testing.T) { } err := mapURI(&s, map[string][]string{"field": {"6"}}) assert.NoError(t, err) - assert.Equal(t, int(6), s.F) + assert.Equal(t, 6, s.F) } func TestMappingForm(t *testing.T) { @@ -142,7 +142,7 @@ func TestMappingForm(t *testing.T) { } err := mapForm(&s, map[string][]string{"field": {"6"}}) assert.NoError(t, err) - assert.Equal(t, int(6), s.F) + assert.Equal(t, 6, s.F) } func TestMapFormWithTag(t *testing.T) { @@ -151,7 +151,7 @@ func TestMapFormWithTag(t *testing.T) { } err := MapFormWithTag(&s, map[string][]string{"field": {"6"}}, "externalTag") assert.NoError(t, err) - assert.Equal(t, int(6), s.F) + assert.Equal(t, 6, s.F) } func TestMappingTime(t *testing.T) { diff --git a/render/render_test.go b/render/render_test.go index a13fff4242..3509db3cec 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -173,7 +173,7 @@ func TestRenderAsciiJSON(t *testing.T) { assert.Equal(t, "application/json", w1.Header().Get("Content-Type")) w2 := httptest.NewRecorder() - data2 := float64(3.1415926) + data2 := 3.1415926 err = (AsciiJSON{data2}).Render(w2) assert.NoError(t, err) From 51aea73ba0f125f6cacc3b4b695efdf21d9c634f Mon Sep 17 00:00:00 2001 From: Jesse <1430482733@qq.com> Date: Thu, 20 Oct 2022 00:49:19 +0800 Subject: [PATCH 167/346] fix: modify interface check way (#3327) --- binding/default_validator.go | 2 +- context_test.go | 2 +- errors.go | 2 +- gin.go | 2 +- response_writer.go | 2 +- routergroup.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/binding/default_validator.go b/binding/default_validator.go index c03afe75be..e216b8546c 100644 --- a/binding/default_validator.go +++ b/binding/default_validator.go @@ -43,7 +43,7 @@ func (err SliceValidationError) Error() string { } } -var _ StructValidator = &defaultValidator{} +var _ StructValidator = (*defaultValidator)(nil) // ValidateStruct receives any kind of type, but only performed struct or pointer to struct type. func (v *defaultValidator) ValidateStruct(obj any) error { diff --git a/context_test.go b/context_test.go index b3e81c1482..cc55cb012d 100644 --- a/context_test.go +++ b/context_test.go @@ -30,7 +30,7 @@ import ( "google.golang.org/protobuf/proto" ) -var _ context.Context = &Context{} +var _ context.Context = (*Context)(nil) // Unit tests TODO // func (c *Context) File(filepath string) { diff --git a/errors.go b/errors.go index ca2bfc3fdc..06b53c28b3 100644 --- a/errors.go +++ b/errors.go @@ -39,7 +39,7 @@ type Error struct { type errorMsgs []*Error -var _ error = &Error{} +var _ error = (*Error)(nil) // SetType sets the error's type. func (msg *Error) SetType(flags ErrorType) *Error { diff --git a/gin.go b/gin.go index f9324299b7..a2e2e67c13 100644 --- a/gin.go +++ b/gin.go @@ -166,7 +166,7 @@ type Engine struct { trustedCIDRs []*net.IPNet } -var _ IRouter = &Engine{} +var _ IRouter = (*Engine)(nil) // New returns a new blank Engine instance without any middleware attached. // By default, the configuration is: diff --git a/response_writer.go b/response_writer.go index 77c7ed8fda..43e828d712 100644 --- a/response_writer.go +++ b/response_writer.go @@ -49,7 +49,7 @@ type responseWriter struct { status int } -var _ ResponseWriter = &responseWriter{} +var _ ResponseWriter = (*responseWriter)(nil) func (w *responseWriter) reset(writer http.ResponseWriter) { w.ResponseWriter = writer diff --git a/routergroup.go b/routergroup.go index 27308bc8fd..dfbdd7b8d3 100644 --- a/routergroup.go +++ b/routergroup.go @@ -58,7 +58,7 @@ type RouterGroup struct { root bool } -var _ IRouter = &RouterGroup{} +var _ IRouter = (*RouterGroup)(nil) // Use adds middleware to the group, see example code in GitHub. func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes { From 8b9c55e8b059a1ffcc69e3b5dc0b9d583958811f Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 6 Nov 2022 17:02:40 +0800 Subject: [PATCH 168/346] fix(route): redirectSlash bug (#3227) fixes https://github.com/gin-gonic/gin/issues/2959 fixes https://github.com/gin-gonic/gin/issues/2282 fixes https://github.com/gin-gonic/gin/issues/2211 --- tree.go | 9 ++++++++- tree_test.go | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 956bf4dd3c..3f34b8ee82 100644 --- a/tree.go +++ b/tree.go @@ -107,7 +107,8 @@ func countSections(path string) uint16 { type nodeType uint8 const ( - root nodeType = iota + 1 + static nodeType = iota + root param catchAll ) @@ -173,6 +174,7 @@ walk: child := node{ path: n.path[i:], wildChild: n.wildChild, + nType: static, indices: n.indices, children: n.children, handlers: n.handlers, @@ -604,6 +606,11 @@ walk: // Outer loop for walking the tree return } + if path == "/" && n.nType == static { + value.tsr = true + return + } + // No handle found. Check if a handle for this path + a // trailing slash exists for trailing slash recommendation for i, c := range []byte(n.indices) { diff --git a/tree_test.go b/tree_test.go index 085b58037b..2005738ec6 100644 --- a/tree_test.go +++ b/tree_test.go @@ -684,6 +684,26 @@ func TestTreeRootTrailingSlashRedirect(t *testing.T) { } } +func TestRedirectTrailingSlash(t *testing.T) { + var data = []struct { + path string + }{ + {"/hello/:name"}, + {"/hello/:name/123"}, + {"/hello/:name/234"}, + } + + node := &node{} + for _, item := range data { + node.addRoute(item.path, fakeHandler("test")) + } + + value := node.getValue("/hello/abx/", nil, getSkippedNodes(), false) + if value.tsr != true { + t.Fatalf("want true, is false") + } +} + func TestTreeFindCaseInsensitivePath(t *testing.T) { tree := &node{} From 971fe21876e491f2597a390522adc849272e3bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=93=88=E5=93=88?= <31426858+wanghaha-dev@users.noreply.github.com> Date: Sun, 6 Nov 2022 17:05:10 +0800 Subject: [PATCH 169/346] docs(comment): Modify comment syntax error (#3389) --- CHANGELOG.md | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bc51a8c9b..a682c8c682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,7 +113,7 @@ * chore(performance): improve countParams ([#2378](https://github.com/gin-gonic/gin/pull/2378)) * Remove some functions that have the same effect as the bytes package ([#2387](https://github.com/gin-gonic/gin/pull/2387)) * update:SetMode function ([#2321](https://github.com/gin-gonic/gin/pull/2321)) -* remove a unused type SecureJSONPrefix ([#2391](https://github.com/gin-gonic/gin/pull/2391)) +* remove an unused type SecureJSONPrefix ([#2391](https://github.com/gin-gonic/gin/pull/2391)) * Add a redirect sample for POST method ([#2389](https://github.com/gin-gonic/gin/pull/2389)) * Add CustomRecovery builtin middleware ([#2322](https://github.com/gin-gonic/gin/pull/2322)) * binding: avoid 2038 problem on 32-bit architectures ([#2450](https://github.com/gin-gonic/gin/pull/2450)) diff --git a/README.md b/README.md index 960c66dc1e..760bde0284 100644 --- a/README.md +++ b/README.md @@ -293,7 +293,7 @@ func main() { router := gin.Default() // Query string parameters are parsed using the existing underlying request object. - // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe + // The request responds to an url matching: /welcome?firstname=Jane&lastname=Doe router.GET("/welcome", func(c *gin.Context) { firstname := c.DefaultQuery("firstname", "Guest") lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") @@ -693,7 +693,7 @@ Also, Gin provides two sets of methods for binding: When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`. -You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has a empty value when binding, an error will be returned. +You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has an empty value when binding, an error will be returned. ```go // Binding from JSON @@ -2096,7 +2096,7 @@ func validate(obj interface{}) error { } // Now we can do this!!! -// FormA is a external type that we can't modify it's tag +// FormA is an external type that we can't modify it's tag type FormA struct { FieldA string `url:"field_a"` } From 55e27f12465e058058180280d5f0bdc473eb3302 Mon Sep 17 00:00:00 2001 From: RoCry Date: Sun, 6 Nov 2022 17:08:11 +0800 Subject: [PATCH 170/346] fix(engine): missing route params for CreateTestContext (#2778) (#2803) --- context_test.go | 16 +++++++++++++++- gin.go | 6 +++--- test_helpers.go | 10 +++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/context_test.go b/context_test.go index cc55cb012d..85e0a6161e 100644 --- a/context_test.go +++ b/context_test.go @@ -146,7 +146,7 @@ func TestSaveUploadedCreateFailed(t *testing.T) { func TestContextReset(t *testing.T) { router := New() - c := router.allocateContext() + c := router.allocateContext(0) assert.Equal(t, c.engine, router) c.index = 2 @@ -2354,3 +2354,17 @@ func TestContextAddParam(t *testing.T) { assert.Equal(t, ok, true) assert.Equal(t, value, v) } + +func TestCreateTestContextWithRouteParams(t *testing.T) { + w := httptest.NewRecorder() + engine := New() + engine.GET("/:action/:name", func(ctx *Context) { + ctx.String(http.StatusOK, "%s %s", ctx.Param("action"), ctx.Param("name")) + }) + c := CreateTestContextOnly(w, engine) + c.Request, _ = http.NewRequest(http.MethodGet, "/hello/gin", nil) + engine.HandleContext(c) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "hello gin", w.Body.String()) +} diff --git a/gin.go b/gin.go index a2e2e67c13..35159d03fa 100644 --- a/gin.go +++ b/gin.go @@ -203,7 +203,7 @@ func New() *Engine { } engine.RouterGroup.engine = engine engine.pool.New = func() any { - return engine.allocateContext() + return engine.allocateContext(engine.maxParams) } return engine } @@ -225,8 +225,8 @@ func (engine *Engine) Handler() http.Handler { return h2c.NewHandler(engine, h2s) } -func (engine *Engine) allocateContext() *Context { - v := make(Params, 0, engine.maxParams) +func (engine *Engine) allocateContext(maxParams uint16) *Context { + v := make(Params, 0, maxParams) skippedNodes := make([]skippedNode, 0, engine.maxSections) return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes} } diff --git a/test_helpers.go b/test_helpers.go index b3be93b4ee..7508c5c90a 100644 --- a/test_helpers.go +++ b/test_helpers.go @@ -9,7 +9,15 @@ import "net/http" // CreateTestContext returns a fresh engine and context for testing purposes func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine) { r = New() - c = r.allocateContext() + c = r.allocateContext(0) + c.reset() + c.writermem.reset(w) + return +} + +// CreateTestContextOnly returns a fresh context base on the engine for testing purposes +func CreateTestContextOnly(w http.ResponseWriter, r *Engine) (c *Context) { + c = r.allocateContext(r.maxParams) c.reset() c.writermem.reset(w) return From 3a6865ac03871578cd68dcf2ebe64205b325c1b1 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Sun, 6 Nov 2022 17:09:25 +0800 Subject: [PATCH 171/346] docs(readme): The krakend is rename to lura (#3377) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 760bde0284..8a6b262a59 100644 --- a/README.md +++ b/README.md @@ -2364,7 +2364,7 @@ Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framewor * [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. * [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform. * [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. -* [krakend](https://github.com/devopsfaith/krakend): Ultra performant API Gateway with middlewares. +* [lura](https://github.com/luraproject/lura): Ultra performant API Gateway with middlewares. * [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. * [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes. * [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. From 8edb7a71a17061bbaa85db53bb8ba6daad3c3aa8 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Sun, 6 Nov 2022 17:10:33 +0800 Subject: [PATCH 172/346] docs(readme): Update some go website links (#3376) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8a6b262a59..6445e97d12 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi To install Gin package, you need to install Go and set your Go workspace first. -1. You first need [Go](https://golang.org/) installed (**version 1.16+ is required**), then you can use the below Go command to install Gin. +1. You first need [Go](https://go.dev/) installed (**version 1.16+ is required**), then you can use the below Go command to install Gin. ```sh go get -u github.com/gin-gonic/gin @@ -678,7 +678,7 @@ func main() { To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML, TOML and standard form values (foo=bar&boo=baz). -Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags). +Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://pkg.go.dev/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags). Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`. @@ -1820,7 +1820,7 @@ Alternatives: #### Manually -In case you are using Go 1.8 or a later version, you may not need to use those libraries. Consider using `http.Server`'s built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. The example below describes its usage, and we've got more examples using gin [here](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown). +In case you are using Go 1.8 or a later version, you may not need to use those libraries. Consider using `http.Server`'s built-in [Shutdown()](https://pkg.go.dev/net/http#Server.Shutdown) method for graceful shutdowns. The example below describes its usage, and we've got more examples using gin [here](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown). ```go // +build go1.8 @@ -2116,7 +2116,7 @@ func ListHandler(s *Service) func(ctx *gin.Context) { ### http2 server push -http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information. +http.Pusher is supported only **go1.8+**. See the [golang blog](https://go.dev/blog/h2push) for detail information. ```go package main From aefae309a4fc197ce5d57cd8391562b6d2a63a95 Mon Sep 17 00:00:00 2001 From: jessetang <1430482733@qq.com> Date: Sun, 6 Nov 2022 17:12:11 +0800 Subject: [PATCH 173/346] fix: test fmt.Println replace t.Error (#3328) --- gin_test.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/gin_test.go b/gin_test.go index 4fac677ac6..5ab2430ce6 100644 --- a/gin_test.go +++ b/gin_test.go @@ -73,7 +73,7 @@ func TestLoadHTMLGlobDebugMode(t *testing.T) { res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -83,7 +83,7 @@ func TestLoadHTMLGlobDebugMode(t *testing.T) { func TestH2c(t *testing.T) { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { - fmt.Println(err) + t.Error(err) } r := Default() r.UseH2C = true @@ -93,7 +93,7 @@ func TestH2c(t *testing.T) { go func() { err := http.Serve(ln, r.Handler()) if err != nil { - fmt.Println(err) + t.Log(err) } }() defer ln.Close() @@ -111,7 +111,7 @@ func TestH2c(t *testing.T) { res, err := httpClient.Get(url) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -131,7 +131,7 @@ func TestLoadHTMLGlobTestMode(t *testing.T) { res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -151,7 +151,7 @@ func TestLoadHTMLGlobReleaseMode(t *testing.T) { res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -178,7 +178,7 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) { client := &http.Client{Transport: tr} res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -198,7 +198,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) { res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -229,7 +229,7 @@ func TestLoadHTMLFilesTestMode(t *testing.T) { res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -249,7 +249,7 @@ func TestLoadHTMLFilesDebugMode(t *testing.T) { res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -269,7 +269,7 @@ func TestLoadHTMLFilesReleaseMode(t *testing.T) { res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -296,7 +296,7 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) { client := &http.Client{Transport: tr} res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) @@ -316,7 +316,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) { res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) if err != nil { - fmt.Println(err) + t.Error(err) } resp, _ := ioutil.ReadAll(res.Body) From c4b3c2c23a5ab25a80e8648f504065922631593e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Nov 2022 19:53:08 +0800 Subject: [PATCH 174/346] chore(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1 (#3373) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] 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, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 1c64cf1b15..ebc1bd1a53 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.16 github.com/pelletier/go-toml/v2 v2.0.2 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20221004154528-8021a29435af google.golang.org/protobuf v1.28.1 diff --git a/go.sum b/go.sum index 35bfd2e9a8..acdf805dd6 100644 --- a/go.sum +++ b/go.sum @@ -55,13 +55,15 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M= github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= From b682b8a54e165088efa71d8b941e7ad28cde348b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Nov 2022 19:53:33 +0800 Subject: [PATCH 175/346] chore(deps): bump golangci/golangci-lint-action from 3.2.0 to 3.3.0 (#3372) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.2.0...v3.3.0) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 18b36d0eb1..45ae932e21 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Setup golangci-lint - uses: golangci/golangci-lint-action@v3.2.0 + uses: golangci/golangci-lint-action@v3.3.0 with: version: v1.48.0 args: --verbose From 212267d6716324536be15512c0b6ed080794b798 Mon Sep 17 00:00:00 2001 From: lgbgbl <65756378+lgbgbl@users.noreply.github.com> Date: Tue, 8 Nov 2022 19:54:48 +0800 Subject: [PATCH 176/346] fix: fix typo in comment (#3371) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index b66b8adc15..ac9db17e1c 100644 --- a/context.go +++ b/context.go @@ -558,7 +558,7 @@ func (c *Context) GetPostFormMap(key string) (map[string]string, bool) { return c.get(c.formCache, key) } -// get is an internal method and returns a map which satisfy conditions. +// get is an internal method and returns a map which satisfies conditions. func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) { dicts := make(map[string]string) exist := false From a0acf1df2814fcd828cb2d7128f2f4e2136d3fac Mon Sep 17 00:00:00 2001 From: mstmdev Date: Wed, 9 Nov 2022 14:50:46 +0800 Subject: [PATCH 177/346] docs(readme): Using the embed package as a recommended example that build a single binary with templates (#3379) --- README.md | 72 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 6445e97d12..48a0a13cdb 100644 --- a/README.md +++ b/README.md @@ -1884,48 +1884,56 @@ func main() { ### Build a single binary with templates -You can build a server into a single binary containing templates by using [go-assets][]. - -[go-assets]: https://github.com/jessevdk/go-assets +You can build a server into a single binary containing templates by using the [embed](https://pkg.go.dev/embed) package. ```go +package main + +import ( + "embed" + "html/template" + "net/http" + + "github.com/gin-gonic/gin" +) + +//go:embed assets/* templates/* +var f embed.FS + func main() { - r := gin.New() + router := gin.Default() + templ := template.Must(template.New("").ParseFS(f, "templates/*.tmpl", "templates/foo/*.tmpl")) + router.SetHTMLTemplate(templ) - t, err := loadTemplate() - if err != nil { - panic(err) - } - r.SetHTMLTemplate(t) + // example: /public/assets/images/example.png + router.StaticFS("/public", http.FS(f)) - r.GET("/", func(c *gin.Context) { - c.HTML(http.StatusOK, "/html/index.tmpl",nil) + router.GET("/", func(c *gin.Context) { + c.HTML(http.StatusOK, "index.tmpl", gin.H{ + "title": "Main website", + }) }) - r.Run(":8080") -} -// loadTemplate loads templates embedded by go-assets-builder -func loadTemplate() (*template.Template, error) { - t := template.New("") - for name, file := range Assets.Files { - defer file.Close() - if file.IsDir() || !strings.HasSuffix(name, ".tmpl") { - continue - } - h, err := ioutil.ReadAll(file) - if err != nil { - return nil, err - } - t, err = t.New(name).Parse(string(h)) - if err != nil { - return nil, err - } - } - return t, nil + router.GET("/foo", func(c *gin.Context) { + c.HTML(http.StatusOK, "bar.tmpl", gin.H{ + "title": "Foo website", + }) + }) + + router.GET("favicon.ico", func(c *gin.Context) { + file, _ := f.ReadFile("assets/favicon.ico") + c.Data( + http.StatusOK, + "image/x-icon", + file, + ) + }) + + router.Run(":8080") } ``` -See a complete example in the `https://github.com/gin-gonic/examples/tree/master/assets-in-binary` directory. +See a complete example in the `https://github.com/gin-gonic/examples/tree/master/assets-in-binary/example02` directory. ### Bind form-data request with custom struct From 234a1d33f7b329a6d701a7b249167f72de57c901 Mon Sep 17 00:00:00 2001 From: gobai <38973236+go-bai@users.noreply.github.com> Date: Thu, 17 Nov 2022 22:34:37 +0800 Subject: [PATCH 178/346] docs(readme): Modify sample code bugs (#3394) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48a0a13cdb..65492e52d0 100644 --- a/README.md +++ b/README.md @@ -1854,7 +1854,7 @@ func main() { // Initializing the server in a goroutine so that // it won't block the graceful shutdown handling below go func() { - if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { + if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Printf("listen: %s\n", err) } }() From 6150c488e73518f119cfed53094d6179a0d33bf7 Mon Sep 17 00:00:00 2001 From: Qt Date: Thu, 17 Nov 2022 22:35:55 +0800 Subject: [PATCH 179/346] remove deprecated of package io/ioutil (#3395) --- binding/binding_test.go | 9 ++++----- binding/multipart_form_mapping_test.go | 4 ++-- binding/protobuf.go | 4 ++-- context.go | 5 ++--- gin_integration_test.go | 4 ++-- gin_test.go | 24 ++++++++++++------------ recovery.go | 3 +-- routes_test.go | 5 ++--- 8 files changed, 27 insertions(+), 31 deletions(-) diff --git a/binding/binding_test.go b/binding/binding_test.go index eae278022f..9af4f88aa3 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -9,7 +9,6 @@ import ( "encoding/json" "errors" "io" - "io/ioutil" "mime/multipart" "net/http" "os" @@ -656,12 +655,12 @@ func TestBindingFormFilesMultipart(t *testing.T) { // file from os f, _ := os.Open("form.go") defer f.Close() - fileActual, _ := ioutil.ReadAll(f) + fileActual, _ := io.ReadAll(f) // file from multipart mf, _ := obj.File.Open() defer mf.Close() - fileExpect, _ := ioutil.ReadAll(mf) + fileExpect, _ := io.ReadAll(mf) assert.Equal(t, FormMultipart.Name(), "multipart/form-data") assert.Equal(t, obj.Foo, "bar") @@ -1347,13 +1346,13 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body obj := protoexample.Test{} req := requestWithBody("POST", path, body) - req.Body = ioutil.NopCloser(&hook{}) + req.Body = io.NopCloser(&hook{}) req.Header.Add("Content-Type", MIMEPROTOBUF) err := b.Bind(req, &obj) assert.Error(t, err) invalidobj := FooStruct{} - req.Body = ioutil.NopCloser(strings.NewReader(`{"msg":"hello"}`)) + req.Body = io.NopCloser(strings.NewReader(`{"msg":"hello"}`)) req.Header.Add("Content-Type", MIMEPROTOBUF) err = b.Bind(req, &invalidobj) assert.Error(t, err) diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go index 9932860386..4e97c0f0a6 100644 --- a/binding/multipart_form_mapping_test.go +++ b/binding/multipart_form_mapping_test.go @@ -6,7 +6,7 @@ package binding import ( "bytes" - "io/ioutil" + "io" "mime/multipart" "net/http" "testing" @@ -129,7 +129,7 @@ func assertMultipartFileHeader(t *testing.T, fh *multipart.FileHeader, file test fl, err := fh.Open() assert.NoError(t, err) - body, err := ioutil.ReadAll(fl) + body, err := io.ReadAll(fl) assert.NoError(t, err) assert.Equal(t, string(file.Content), string(body)) diff --git a/binding/protobuf.go b/binding/protobuf.go index 44f2fdb93d..57721fc9f9 100644 --- a/binding/protobuf.go +++ b/binding/protobuf.go @@ -6,7 +6,7 @@ package binding import ( "errors" - "io/ioutil" + "io" "net/http" "google.golang.org/protobuf/proto" @@ -19,7 +19,7 @@ func (protobufBinding) Name() string { } func (b protobufBinding) Bind(req *http.Request, obj any) error { - buf, err := ioutil.ReadAll(req.Body) + buf, err := io.ReadAll(req.Body) if err != nil { return err } diff --git a/context.go b/context.go index ac9db17e1c..c41c71ecf9 100644 --- a/context.go +++ b/context.go @@ -7,7 +7,6 @@ package gin import ( "errors" "io" - "io/ioutil" "log" "math" "mime/multipart" @@ -753,7 +752,7 @@ func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error } } if body == nil { - body, err = ioutil.ReadAll(c.Request.Body) + body, err = io.ReadAll(c.Request.Body) if err != nil { return err } @@ -872,7 +871,7 @@ func (c *Context) GetHeader(key string) string { // GetRawData returns stream data. func (c *Context) GetRawData() ([]byte, error) { - return ioutil.ReadAll(c.Request.Body) + return io.ReadAll(c.Request.Body) } // SetSameSite with cookie diff --git a/gin_integration_test.go b/gin_integration_test.go index b0532a25d1..02b9622119 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -9,7 +9,7 @@ import ( "crypto/tls" "fmt" "html/template" - "io/ioutil" + "io" "net" "net/http" "net/http/httptest" @@ -43,7 +43,7 @@ func testRequest(t *testing.T, params ...string) { assert.NoError(t, err) defer resp.Body.Close() - body, ioerr := ioutil.ReadAll(resp.Body) + body, ioerr := io.ReadAll(resp.Body) assert.NoError(t, ioerr) var responseStatus = "200 OK" diff --git a/gin_test.go b/gin_test.go index 5ab2430ce6..8825ac7ef8 100644 --- a/gin_test.go +++ b/gin_test.go @@ -8,7 +8,7 @@ import ( "crypto/tls" "fmt" "html/template" - "io/ioutil" + "io" "net" "net/http" "net/http/httptest" @@ -76,7 +76,7 @@ func TestLoadHTMLGlobDebugMode(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } @@ -114,7 +114,7 @@ func TestH2c(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } @@ -134,7 +134,7 @@ func TestLoadHTMLGlobTestMode(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } @@ -154,7 +154,7 @@ func TestLoadHTMLGlobReleaseMode(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } @@ -181,7 +181,7 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } @@ -201,7 +201,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "Date: 2017/07/01", string(resp)) } @@ -232,7 +232,7 @@ func TestLoadHTMLFilesTestMode(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } @@ -252,7 +252,7 @@ func TestLoadHTMLFilesDebugMode(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } @@ -272,7 +272,7 @@ func TestLoadHTMLFilesReleaseMode(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } @@ -299,7 +299,7 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } @@ -319,7 +319,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) { t.Error(err) } - resp, _ := ioutil.ReadAll(res.Body) + resp, _ := io.ReadAll(res.Body) assert.Equal(t, "Date: 2017/07/01", string(resp)) } diff --git a/recovery.go b/recovery.go index 05b30d954d..3a90f2505c 100644 --- a/recovery.go +++ b/recovery.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "log" "net" "net/http" @@ -121,7 +120,7 @@ func stack(skip int) []byte { // Print this much at least. If we can't find the source, it won't show. fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) if file != lastFile { - data, err := ioutil.ReadFile(file) + data, err := os.ReadFile(file) if err != nil { continue } diff --git a/routes_test.go b/routes_test.go index d7034b2292..cd8cf14145 100644 --- a/routes_test.go +++ b/routes_test.go @@ -6,7 +6,6 @@ package gin import ( "fmt" - "io/ioutil" "net/http" "net/http/httptest" "os" @@ -294,7 +293,7 @@ func TestRouteParamsByNameWithExtraSlash(t *testing.T) { func TestRouteStaticFile(t *testing.T) { // SETUP file testRoot, _ := os.Getwd() - f, err := ioutil.TempFile(testRoot, "") + f, err := os.CreateTemp(testRoot, "") if err != nil { t.Error(err) } @@ -329,7 +328,7 @@ func TestRouteStaticFile(t *testing.T) { func TestRouteStaticFileFS(t *testing.T) { // SETUP file testRoot, _ := os.Getwd() - f, err := ioutil.TempFile(testRoot, "") + f, err := os.CreateTemp(testRoot, "") if err != nil { t.Error(err) } From c629689591bf3f50650292f382f16ec9b6952904 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Thu, 17 Nov 2022 22:37:50 +0800 Subject: [PATCH 180/346] docs(readme): Add the TOML rendering example (#3400) --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 65492e52d0..103a53c74a 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [Bind Header](#bind-header) - [Bind HTML checkboxes](#bind-html-checkboxes) - [Multipart/Urlencoded binding](#multiparturlencoded-binding) - - [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering) + - [XML, JSON, YAML, TOML and ProtoBuf rendering](#xml-json-yaml-toml-and-protobuf-rendering) - [SecureJSON](#securejson) - [JSONP](#jsonp) - [AsciiJSON](#asciijson) @@ -1114,7 +1114,7 @@ Test it with: curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile ``` -### XML, JSON, YAML and ProtoBuf rendering +### XML, JSON, YAML, TOML and ProtoBuf rendering ```go func main() { @@ -1148,6 +1148,10 @@ func main() { c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) }) + r.GET("/someTOML", func(c *gin.Context) { + c.TOML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + r.GET("/someProtoBuf", func(c *gin.Context) { reps := []int64{int64(1), int64(2)} label := "test" From 8fe209a447426233f55093af1d51d3964ca10654 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 22:38:19 +0800 Subject: [PATCH 181/346] chore(deps): bump golangci/golangci-lint-action from 3.3.0 to 3.3.1 (#3399) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 45ae932e21..7a4e61c617 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Setup golangci-lint - uses: golangci/golangci-lint-action@v3.3.0 + uses: golangci/golangci-lint-action@v3.3.1 with: version: v1.48.0 args: --verbose From 80cd679c43a3d6ed03957b6a3614f97d0af0751c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Nov 2022 15:34:18 +0800 Subject: [PATCH 182/346] chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.2 to 2.0.6 (#3408) Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.2 to 2.0.6. - [Release notes](https://github.com/pelletier/go-toml/releases) - [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml) - [Commits](https://github.com/pelletier/go-toml/compare/v2.0.2...v2.0.6) --- updated-dependencies: - dependency-name: github.com/pelletier/go-toml/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index ebc1bd1a53..200a4403f7 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/goccy/go-json v0.9.11 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.16 - github.com/pelletier/go-toml/v2 v2.0.2 + github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.0.0-20221004154528-8021a29435af diff --git a/go.sum b/go.sum index acdf805dd6..574e4a9ae7 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= -github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -60,7 +60,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= From cc367f9125516313a4b8d2da1eed5527b5420dbc Mon Sep 17 00:00:00 2001 From: Cookiery <33125275+Cookiery@users.noreply.github.com> Date: Thu, 1 Dec 2022 13:15:31 +0800 Subject: [PATCH 183/346] docs(context): #3369 modify the annotation about Context.Param() (#3414) --- context.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/context.go b/context.go index c41c71ecf9..737e4d7a37 100644 --- a/context.go +++ b/context.go @@ -386,7 +386,9 @@ func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) // // router.GET("/user/:id", func(c *gin.Context) { // // a GET request to /user/john -// id := c.Param("id") // id == "john" +// id := c.Param("id") // id == "/john" +// // a GET request to /user/john/ +// id := c.Param("id") // id == "/john/" // }) func (c *Context) Param(key string) string { return c.Params.ByName(key) From 483ac2a63bff7e1104fbe236dacff774c09bc24d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 14:43:06 +0800 Subject: [PATCH 184/346] chore(deps): bump goreleaser/goreleaser-action from 3 to 4 (#3441) Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 3 to 4. - [Release notes](https://github.com/goreleaser/goreleaser-action/releases) - [Commits](https://github.com/goreleaser/goreleaser-action/compare/v3...v4) --- updated-dependencies: - dependency-name: goreleaser/goreleaser-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 654585b91d..3af3a455e1 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -24,7 +24,7 @@ jobs: go-version: 1.17 - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v3 + uses: goreleaser/goreleaser-action@v4 with: # either 'goreleaser' (default) or 'goreleaser-pro' distribution: goreleaser From f551d7d8c2930c0140781a284f4372490c365a43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 14:43:42 +0800 Subject: [PATCH 185/346] chore(deps): bump github.com/bytedance/sonic from 1.4.0 to 1.6.0 (#3442) Bumps [github.com/bytedance/sonic](https://github.com/bytedance/sonic) from 1.4.0 to 1.6.0. - [Release notes](https://github.com/bytedance/sonic/releases) - [Commits](https://github.com/bytedance/sonic/compare/v1.4.0...v1.6.0) --- updated-dependencies: - dependency-name: github.com/bytedance/sonic dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 19 +++++-------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 200a4403f7..1929c9591d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( - github.com/bytedance/sonic v1.4.0 + github.com/bytedance/sonic v1.6.0 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.11.1 github.com/goccy/go-json v0.9.11 @@ -18,7 +18,7 @@ require ( ) require ( - github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect diff --git a/go.sum b/go.sum index 574e4a9ae7..054058aa27 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,9 @@ -github.com/bytedance/sonic v1.4.0 h1:d6vgPhwgHfpmEiz/9Fzea9fGzWY7RO1TQEySBiRwDLY= -github.com/bytedance/sonic v1.4.0/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.6.0 h1:j90DM/Ss1bmySEQYL2U4jRsUjJ+chASzCCZYxohJR60= +github.com/bytedance/sonic v1.6.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a h1:lmGPzuocwDxoPAMr9h16zoJY/USZR9jIh99nrmKk1uI= -github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -17,7 +18,6 @@ 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.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -63,15 +63,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M= -github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc= -github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= From d4caeee7c7672dca05e5033e9e30204b52d398e6 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Wed, 21 Dec 2022 14:44:36 +0800 Subject: [PATCH 186/346] Fix the GO-2022-1144 vulnerability (#3432) --- go.mod | 6 +++--- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 1929c9591d..7a355e981d 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 github.com/ugorji/go/codec v1.2.7 - golang.org/x/net v0.0.0-20221004154528-8021a29435af + golang.org/x/net v0.4.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -30,7 +30,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 054058aa27..516c053c65 100644 --- a/go.sum +++ b/go.sum @@ -74,18 +74,20 @@ golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUu golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= -golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 2285aa5430fb5e68bb55a89009fc4f5dd261b466 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Wed, 21 Dec 2022 15:02:00 +0800 Subject: [PATCH 187/346] docs(readme): release v1.8.2 version (#3420) * docs(readme): release v1.8.2 version * Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ version.go | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a682c8c682..2ab5617924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Gin ChangeLog +## Gin v1.8.2 + +### Bugs + +* fix(route): redirectSlash bug ([#3227]((https://github.com/gin-gonic/gin/pull/3227))) +* fix(engine): missing route params for CreateTestContext ([#2778]((https://github.com/gin-gonic/gin/pull/2778))) ([#2803]((https://github.com/gin-gonic/gin/pull/2803))) + +### Security + +* Fix the GO-2022-1144 vulnerability ([#3432]((https://github.com/gin-gonic/gin/pull/3432))) + ## Gin v1.8.1 ### ENHANCEMENTS diff --git a/version.go b/version.go index 632ca7d1d6..37e27f27a5 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.8.1" +const Version = "v1.8.2" From 297b664cf8bbb3b9434e473cf976c98f00528ff7 Mon Sep 17 00:00:00 2001 From: lgbgbl <65756378+lgbgbl@users.noreply.github.com> Date: Thu, 22 Dec 2022 23:17:19 +0800 Subject: [PATCH 188/346] refactor: avoid calling strings.ToLower twice (#3433) --- recovery.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/recovery.go b/recovery.go index 3a90f2505c..2955c03a0e 100644 --- a/recovery.go +++ b/recovery.go @@ -62,7 +62,9 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc { if ne, ok := err.(*net.OpError); 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") { + seStr := strings.ToLower(se.Error()) + if strings.Contains(seStr, "broken pipe") || + strings.Contains(seStr, "connection reset by peer") { brokenPipe = true } } From e868fd1d3d350d7186efcdf71f24f56d8e2b8408 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Thu, 22 Dec 2022 23:18:47 +0800 Subject: [PATCH 189/346] test(TOML): Add some tests for the TOML render (#3401) --- render/render_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/render/render_test.go b/render/render_test.go index 3509db3cec..c5c5375f53 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -8,6 +8,7 @@ import ( "encoding/xml" "errors" "html/template" + "net" "net/http" "net/http/httptest" "strconv" @@ -254,6 +255,27 @@ func TestRenderYAMLFail(t *testing.T) { assert.Error(t, err) } +func TestRenderTOML(t *testing.T) { + w := httptest.NewRecorder() + data := map[string]any{ + "foo": "bar", + "html": "", + } + (TOML{data}).WriteContentType(w) + assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type")) + + err := (TOML{data}).Render(w) + assert.NoError(t, err) + assert.Equal(t, "foo = 'bar'\nhtml = ''\n", w.Body.String()) + assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type")) +} + +func TestRenderTOMLFail(t *testing.T) { + w := httptest.NewRecorder() + err := (TOML{net.IPv4bcast}).Render(w) + assert.Error(t, err) +} + // test Protobuf rendering func TestRenderProtoBuf(t *testing.T) { w := httptest.NewRecorder() From 8659ab573cf7d26b2fa2a41e90075d84606188f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Dec 2022 13:49:44 +0800 Subject: [PATCH 190/346] chore(deps): bump github.com/goccy/go-json from 0.9.11 to 0.10.0 (#3424) Bumps [github.com/goccy/go-json](https://github.com/goccy/go-json) from 0.9.11 to 0.10.0. - [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.9.11...v0.10.0) --- updated-dependencies: - dependency-name: github.com/goccy/go-json dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] 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 7a355e981d..e969833934 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/bytedance/sonic v1.6.0 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.11.1 - github.com/goccy/go-json v0.9.11 + github.com/goccy/go-json v0.10.0 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.16 github.com/pelletier/go-toml/v2 v2.0.6 diff --git a/go.sum b/go.sum index 516c053c65..6d8df64a0f 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,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.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= +github.com/goccy/go-json v0.10.0/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 82e1c53cc0a3b0bdb7baab6b95d05a7ec796f7a2 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 2 Jan 2023 10:40:25 +0800 Subject: [PATCH 191/346] docs(readme): move more example to docs/doc.md (#3449) --- README.md | 2352 ++------------------------------------------------- docs/doc.md | 2246 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2321 insertions(+), 2277 deletions(-) create mode 100644 docs/doc.md diff --git a/README.md b/README.md index 103a53c74a..eccf814c2e 100644 --- a/README.md +++ b/README.md @@ -12,106 +12,47 @@ [![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases) [![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/gin-gonic/gin)](https://www.tickgit.com/browse?repo=github.com/gin-gonic/gin) -Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. +Gin is a web framework written in [Go](https://go.dev/). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. -## Contents +**The key features of Gin are:** -- [Gin Web Framework](#gin-web-framework) - - [Contents](#contents) - - [Installation](#installation) - - [Quick start](#quick-start) - - [Benchmarks](#benchmarks) - - [Gin v1. stable](#gin-v1-stable) - - [Build with json replacement](#build-with-json-replacement) - - [Build without `MsgPack` rendering feature](#build-without-msgpack-rendering-feature) - - [API Examples](#api-examples) - - [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options) - - [Parameters in path](#parameters-in-path) - - [Querystring parameters](#querystring-parameters) - - [Multipart/Urlencoded Form](#multiparturlencoded-form) - - [Another example: query + post form](#another-example-query--post-form) - - [Map as querystring or postform parameters](#map-as-querystring-or-postform-parameters) - - [Upload files](#upload-files) - - [Single file](#single-file) - - [Multiple files](#multiple-files) - - [Grouping routes](#grouping-routes) - - [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default) - - [Using middleware](#using-middleware) - - [Custom Recovery behavior](#custom-recovery-behavior) - - [How to write log file](#how-to-write-log-file) - - [Custom Log Format](#custom-log-format) - - [Controlling Log output coloring](#controlling-log-output-coloring) - - [Model binding and validation](#model-binding-and-validation) - - [Custom Validators](#custom-validators) - - [Only Bind Query String](#only-bind-query-string) - - [Bind Query String or Post Data](#bind-query-string-or-post-data) - - [Bind Uri](#bind-uri) - - [Bind Header](#bind-header) - - [Bind HTML checkboxes](#bind-html-checkboxes) - - [Multipart/Urlencoded binding](#multiparturlencoded-binding) - - [XML, JSON, YAML, TOML and ProtoBuf rendering](#xml-json-yaml-toml-and-protobuf-rendering) - - [SecureJSON](#securejson) - - [JSONP](#jsonp) - - [AsciiJSON](#asciijson) - - [PureJSON](#purejson) - - [Serving static files](#serving-static-files) - - [Serving data from file](#serving-data-from-file) - - [Serving data from reader](#serving-data-from-reader) - - [HTML rendering](#html-rendering) - - [Custom Template renderer](#custom-template-renderer) - - [Custom Delimiters](#custom-delimiters) - - [Custom Template Funcs](#custom-template-funcs) - - [Multitemplate](#multitemplate) - - [Redirects](#redirects) - - [Custom Middleware](#custom-middleware) - - [Using BasicAuth() middleware](#using-basicauth-middleware) - - [Goroutines inside a middleware](#goroutines-inside-a-middleware) - - [Custom HTTP configuration](#custom-http-configuration) - - [Support Let's Encrypt](#support-lets-encrypt) - - [Run multiple service using Gin](#run-multiple-service-using-gin) - - [Graceful shutdown or restart](#graceful-shutdown-or-restart) - - [Third-party packages](#third-party-packages) - - [Manually](#manually) - - [Build a single binary with templates](#build-a-single-binary-with-templates) - - [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct) - - [Try to bind body into different structs](#try-to-bind-body-into-different-structs) - - [Bind form-data request with custom struct and custom tag](#bind-form-data-request-with-custom-struct-and-custom-tag) - - [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](#dont-trust-all-proxies) - - [Testing](#testing) - - [Users](#users) +- Zero allocation router +- Fast +- Middleware support +- Crash-free +- JSON validation +- Routes grouping +- Error management +- Rendering built-in +- Extendable -## Installation -To install Gin package, you need to install Go and set your Go workspace first. +## Getting started -1. You first need [Go](https://go.dev/) installed (**version 1.16+ is required**), then you can use the below Go command to install Gin. +### Prerequisites -```sh -go get -u github.com/gin-gonic/gin -``` +- **[Go](https://go.dev/)**: ~~any one of the **three latest major** [releases](https://go.dev/doc/devel/release)~~ (now version **1.16+** is required). -2. Import it in your code: +### Getting Gin -```go +With [Go module](https://github.com/golang/go/wiki/Modules) support, simply add the following import + +``` import "github.com/gin-gonic/gin" ``` -3. (Optional) Import `net/http`. This is required for example if using constants such as `http.StatusOK`. - -```go -import "net/http" -``` +to your code, and then `go [build|run|test]` will automatically fetch the necessary dependencies. -## Quick start +Otherwise, run the following Go command to install the `gin` package: ```sh -# assume the following codes in example.go file -$ cat example.go +$ go get -u github.com/gin-gonic/gin ``` +### Running Gin + +First you need to import Gin package for using Gin, one simplest example likes the follow `example.go`: + ```go package main @@ -132,16 +73,48 @@ func main() { } ``` +And use the Go command to run the demo: + ``` -# run example.go and visit 0.0.0.0:8080/ping (for windows "localhost:8080/ping") on browser +# run example.go and visit 0.0.0.0:8080/ping on browser $ go run example.go ``` -## Benchmarks +### Learn more examples + +#### Quick Start + +Learn and practice more examples, please read the [Gin Quick Start](docs/doc.md) which includes API examples and builds tag. + +#### Examples + +A number of ready-to-run examples demonstrating various use cases of Gin on the [Gin examples](https://github.com/gin-gonic/examples) repository. -Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter) -[See all benchmarks](/BENCHMARKS.md) +## Documentation + +See [API documentation and descriptions](https://godoc.org/github.com/gin-gonic/gin) for package. + +All documentation is available on the Gin website. + +- [English](https://gin-gonic.com/docs/) +- [简体中文](https://gin-gonic.com/zh-cn/docs/) +- [繁體中文](https://gin-gonic.com/zh-tw/docs/) +- [日本語](https://gin-gonic.com/ja/docs/) +- [Español](https://gin-gonic.com/es/docs/) +- [한국어](https://gin-gonic.com/ko-kr/docs/) +- [Turkish](https://gin-gonic.com/tr/docs/) +- [Persian](https://gin-gonic.com/fa/docs/) + +### Articles about Gin + +A curated list of awesome Gin framework. + +- [Tutorial: Developing a RESTful API with Go and Gin](https://go.dev/doc/tutorial/web-service-gin) + +## Benchmarks + +Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter), [see all benchmarks details](/BENCHMARKS.md). | Benchmark name | (1) | (2) | (3) | (4) | | ------------------------------ | ---------:| ---------------:| ------------:| ---------------:| @@ -181,2202 +154,27 @@ Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httpr - (3): Heap Memory (B/op), lower is better - (4): Average Allocations per Repetition (allocs/op), lower is better -## Gin v1. stable - -- [x] Zero allocation router. -- [x] Still the fastest http router and framework. From routing to writing. -- [x] Complete suite of unit tests. -- [x] Battle tested. -- [x] API frozen, new releases will not break your code. - -## Build with json replacement - -Gin uses `encoding/json` as default json package but you can change it by build from other tags. - -[jsoniter](https://github.com/json-iterator/go) - -```sh -go build -tags=jsoniter . -``` - -[go-json](https://github.com/goccy/go-json) - -```sh -go build -tags=go_json . -``` - -[sonic](https://github.com/bytedance/sonic) (you have to ensure that your cpu support avx instruction.) - -```sh -$ go build -tags="sonic avx" . -``` - -## Build without `MsgPack` rendering feature - -Gin enables `MsgPack` rendering feature by default. But you can disable this feature by specifying `nomsgpack` build tag. - -```sh -go build -tags=nomsgpack . -``` - -This is useful to reduce the binary size of executable files. See the [detail information](https://github.com/gin-gonic/gin/pull/1852). - -## API Examples - -You can find a number of ready-to-run examples at [Gin examples repository](https://github.com/gin-gonic/examples). - -### Using GET, POST, PUT, PATCH, DELETE and OPTIONS - -```go -func main() { - // Creates a gin router with default middleware: - // logger and recovery (crash-free) middleware - router := gin.Default() - - router.GET("/someGet", getting) - router.POST("/somePost", posting) - router.PUT("/somePut", putting) - router.DELETE("/someDelete", deleting) - router.PATCH("/somePatch", patching) - router.HEAD("/someHead", head) - router.OPTIONS("/someOptions", options) - - // By default it serves on :8080 unless a - // PORT environment variable was defined. - router.Run() - // router.Run(":3000") for a hard coded port -} -``` - -### Parameters in path - -```go -func main() { - router := gin.Default() - - // This handler will match /user/john but will not match /user/ or /user - router.GET("/user/:name", func(c *gin.Context) { - name := c.Param("name") - c.String(http.StatusOK, "Hello %s", name) - }) - - // However, this one will match /user/john/ and also /user/john/send - // If no other routers match /user/john, it will redirect to /user/john/ - router.GET("/user/:name/*action", func(c *gin.Context) { - name := c.Param("name") - action := c.Param("action") - message := name + " is " + action - c.String(http.StatusOK, message) - }) - - // For each matched request Context will hold the route definition - router.POST("/user/:name/*action", func(c *gin.Context) { - b := c.FullPath() == "/user/:name/*action" // true - c.String(http.StatusOK, "%t", b) - }) - - // This handler will add a new router for /user/groups. - // Exact routes are resolved before param routes, regardless of the order they were defined. - // Routes starting with /user/groups are never interpreted as /user/:name/... routes - router.GET("/user/groups", func(c *gin.Context) { - c.String(http.StatusOK, "The available groups are [...]") - }) - - router.Run(":8080") -} -``` - -### Querystring parameters - -```go -func main() { - router := gin.Default() - - // Query string parameters are parsed using the existing underlying request object. - // The request responds to an url matching: /welcome?firstname=Jane&lastname=Doe - router.GET("/welcome", func(c *gin.Context) { - firstname := c.DefaultQuery("firstname", "Guest") - lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") - - c.String(http.StatusOK, "Hello %s %s", firstname, lastname) - }) - router.Run(":8080") -} -``` - -### Multipart/Urlencoded Form - -```go -func main() { - router := gin.Default() - - router.POST("/form_post", func(c *gin.Context) { - message := c.PostForm("message") - nick := c.DefaultPostForm("nick", "anonymous") - - c.JSON(http.StatusOK, gin.H{ - "status": "posted", - "message": message, - "nick": nick, - }) - }) - router.Run(":8080") -} -``` - -### Another example: query + post form - -```sh -POST /post?id=1234&page=1 HTTP/1.1 -Content-Type: application/x-www-form-urlencoded - -name=manu&message=this_is_great -``` - -```go -func main() { - router := gin.Default() - - router.POST("/post", func(c *gin.Context) { - - id := c.Query("id") - page := c.DefaultQuery("page", "0") - name := c.PostForm("name") - message := c.PostForm("message") - - fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) - }) - router.Run(":8080") -} -``` - -```sh -id: 1234; page: 1; name: manu; message: this_is_great -``` - -### Map as querystring or postform parameters - -```sh -POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1 -Content-Type: application/x-www-form-urlencoded - -names[first]=thinkerou&names[second]=tianou -``` - -```go -func main() { - router := gin.Default() - - router.POST("/post", func(c *gin.Context) { - - ids := c.QueryMap("ids") - names := c.PostFormMap("names") - - fmt.Printf("ids: %v; names: %v", ids, names) - }) - router.Run(":8080") -} -``` - -```sh -ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou] -``` - -### Upload files - -#### Single file - -References issue [#774](https://github.com/gin-gonic/gin/issues/774) and detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/single). -`file.Filename` **SHOULD NOT** be trusted. See [`Content-Disposition` on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Directives) and [#1693](https://github.com/gin-gonic/gin/issues/1693) +## Middlewares -> The filename is always optional and must not be used blindly by the application: path information should be stripped, and conversion to the server file system rules should be done. +You can find many useful Gin middlewares at [gin-contrib](https://github.com/gin-contrib). -```go -func main() { - router := gin.Default() - // 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 - file, _ := c.FormFile("file") - log.Println(file.Filename) - - // Upload the file to specific dst. - c.SaveUploadedFile(file, dst) - - c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) - }) - router.Run(":8080") -} -``` - -How to `curl`: - -```bash -curl -X POST http://localhost:8080/upload \ - -F "file=@/Users/appleboy/test.zip" \ - -H "Content-Type: multipart/form-data" -``` - -#### Multiple files - -See the detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/multiple). - -```go -func main() { - router := gin.Default() - // 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) { - // Multipart form - form, _ := c.MultipartForm() - files := form.File["upload[]"] - - for _, file := range files { - log.Println(file.Filename) - - // Upload the file to specific dst. - c.SaveUploadedFile(file, dst) - } - c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) - }) - router.Run(":8080") -} -``` - -How to `curl`: - -```bash -curl -X POST http://localhost:8080/upload \ - -F "upload[]=@/Users/appleboy/test1.zip" \ - -F "upload[]=@/Users/appleboy/test2.zip" \ - -H "Content-Type: multipart/form-data" -``` - -### Grouping routes - -```go -func main() { - router := gin.Default() - - // Simple group: v1 - v1 := router.Group("/v1") - { - v1.POST("/login", loginEndpoint) - v1.POST("/submit", submitEndpoint) - v1.POST("/read", readEndpoint) - } - - // Simple group: v2 - v2 := router.Group("/v2") - { - v2.POST("/login", loginEndpoint) - v2.POST("/submit", submitEndpoint) - v2.POST("/read", readEndpoint) - } - - router.Run(":8080") -} -``` - -### Blank Gin without middleware by default - -Use - -```go -r := gin.New() -``` - -instead of - -```go -// Default With the Logger and Recovery middleware already attached -r := gin.Default() -``` - -### Using middleware - -```go -func main() { - // Creates a router without any middleware by default - r := gin.New() - - // Global middleware - // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. - // By default gin.DefaultWriter = os.Stdout - r.Use(gin.Logger()) - - // Recovery middleware recovers from any panics and writes a 500 if there was one. - r.Use(gin.Recovery()) - - // Per route middleware, you can add as many as you desire. - r.GET("/benchmark", MyBenchLogger(), benchEndpoint) - - // Authorization group - // authorized := r.Group("/", AuthRequired()) - // exactly the same as: - authorized := r.Group("/") - // per group middleware! in this case we use the custom created - // AuthRequired() middleware just in the "authorized" group. - authorized.Use(AuthRequired()) - { - authorized.POST("/login", loginEndpoint) - authorized.POST("/submit", submitEndpoint) - authorized.POST("/read", readEndpoint) - - // nested group - testing := authorized.Group("testing") - // visit 0.0.0.0:8080/testing/analytics - testing.GET("/analytics", analyticsEndpoint) - } - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") -} -``` - -### Custom Recovery behavior - -```go -func main() { - // Creates a router without any middleware by default - r := gin.New() - - // Global middleware - // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. - // By default gin.DefaultWriter = os.Stdout - r.Use(gin.Logger()) - - // Recovery middleware recovers from any panics and writes a 500 if there was one. - r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) { - if err, ok := recovered.(string); ok { - c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) - } - c.AbortWithStatus(http.StatusInternalServerError) - })) - - r.GET("/panic", func(c *gin.Context) { - // panic with a string -- the custom middleware could save this to a database or report it to the user - panic("foo") - }) - - r.GET("/", func(c *gin.Context) { - c.String(http.StatusOK, "ohai") - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") -} -``` - -### How to write log file - -```go -func main() { - // Disable Console Color, you don't need console color when writing the logs to file. - gin.DisableConsoleColor() - - // Logging to a file. - f, _ := os.Create("gin.log") - gin.DefaultWriter = io.MultiWriter(f) - - // Use the following code if you need to write the logs to file and console at the same time. - // gin.DefaultWriter = io.MultiWriter(f, os.Stdout) - - router := gin.Default() - router.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) - -    router.Run(":8080") -} -``` - -### Custom Log Format - -```go -func main() { - router := gin.New() - - // LoggerWithFormatter middleware will write the logs to gin.DefaultWriter - // By default gin.DefaultWriter = os.Stdout - router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { - - // your custom format - return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", - param.ClientIP, - param.TimeStamp.Format(time.RFC1123), - param.Method, - param.Path, - param.Request.Proto, - param.StatusCode, - param.Latency, - param.Request.UserAgent(), - param.ErrorMessage, - ) - })) - router.Use(gin.Recovery()) - - router.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) - - router.Run(":8080") -} -``` - -Sample Output - -```sh -::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" " -``` - -### Controlling Log output coloring - -By default, logs output on console should be colorized depending on the detected TTY. - -Never colorize logs: - -```go -func main() { - // Disable log's color - gin.DisableConsoleColor() - - // Creates a gin router with default middleware: - // logger and recovery (crash-free) middleware - router := gin.Default() - - router.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) - - router.Run(":8080") -} -``` - -Always colorize logs: - -```go -func main() { - // Force log's color - gin.ForceConsoleColor() - - // Creates a gin router with default middleware: - // logger and recovery (crash-free) middleware - router := gin.Default() - - router.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) - - router.Run(":8080") -} -``` - -### Model binding and validation - -To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML, TOML and standard form values (foo=bar&boo=baz). -Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://pkg.go.dev/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags). - -Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`. +## Users -Also, Gin provides two sets of methods for binding: +Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. -- **Type** - Must bind - - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader`, `BindTOML` - - **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method. -- **Type** - Should bind - - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader`, `ShouldBindTOML`, - - **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately. +* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. +* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform. +* [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. +* [lura](https://github.com/luraproject/lura): Ultra performant API Gateway with middlewares. +* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. +* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes. +* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. -When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`. -You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has an empty value when binding, an error will be returned. +## Contributing -```go -// Binding from JSON -type Login struct { - User string `form:"user" json:"user" xml:"user" binding:"required"` - Password string `form:"password" json:"password" xml:"password" binding:"required"` -} +Gin is the work of hundreds of contributors. We appreciate your help! -func main() { - router := gin.Default() - - // Example for binding JSON ({"user": "manu", "password": "123"}) - router.POST("/loginJSON", func(c *gin.Context) { - var json Login - if err := c.ShouldBindJSON(&json); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - if json.User != "manu" || json.Password != "123" { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - return - } - - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - }) - - // Example for binding XML ( - // - // - // manu - // 123 - // ) - router.POST("/loginXML", func(c *gin.Context) { - var xml Login - if err := c.ShouldBindXML(&xml); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - if xml.User != "manu" || xml.Password != "123" { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - return - } - - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - }) - - // Example for binding a HTML form (user=manu&password=123) - router.POST("/loginForm", func(c *gin.Context) { - var form Login - // This will infer what binder to use depending on the content-type header. - if err := c.ShouldBind(&form); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - if form.User != "manu" || form.Password != "123" { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - return - } - - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - }) - - // Listen and serve on 0.0.0.0:8080 - router.Run(":8080") -} -``` - -Sample request - -```sh -$ curl -v -X POST \ - http://localhost:8080/loginJSON \ - -H 'content-type: application/json' \ - -d '{ "user": "manu" }' -> POST /loginJSON HTTP/1.1 -> Host: localhost:8080 -> User-Agent: curl/7.51.0 -> Accept: */* -> content-type: application/json -> Content-Length: 18 -> -* upload completely sent off: 18 out of 18 bytes -< HTTP/1.1 400 Bad Request -< Content-Type: application/json; charset=utf-8 -< Date: Fri, 04 Aug 2017 03:51:31 GMT -< Content-Length: 100 -< -{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"} -``` - -Skip validate: when running the above example using the above the `curl` command, it returns error. Because the example use `binding:"required"` for `Password`. If use `binding:"-"` for `Password`, then it will not return error when running the above example again. - -### Custom Validators - -It is also possible to register custom validators. See the [example code](https://github.com/gin-gonic/examples/tree/master/custom-validation/server.go). - -```go -package main - -import ( - "net/http" - "time" - - "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/binding" - "github.com/go-playground/validator/v10" -) - -// Booking contains binded and validated data. -type Booking struct { - CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` - CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"` -} - -var bookableDate validator.Func = func(fl validator.FieldLevel) bool { - date, ok := fl.Field().Interface().(time.Time) - if ok { - today := time.Now() - if today.After(date) { - return false - } - } - return true -} - -func main() { - route := gin.Default() - - if v, ok := binding.Validator.Engine().(*validator.Validate); ok { - v.RegisterValidation("bookabledate", bookableDate) - } - - route.GET("/bookable", getBookable) - route.Run(":8085") -} - -func getBookable(c *gin.Context) { - var b Booking - if err := c.ShouldBindWith(&b, binding.Query); err == nil { - c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"}) - } else { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - } -} -``` - -```console -$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17" -{"message":"Booking dates are valid!"} - -$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09" -{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"} - -$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10" -{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}% -``` - -[Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registered this way. -See the [struct-lvl-validation example](https://github.com/gin-gonic/examples/tree/master/struct-lvl-validations) to learn more. - -### Only Bind Query String - -`ShouldBindQuery` function only binds the query params and not the post data. See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017). - -```go -package main - -import ( - "log" - "net/http" - - "github.com/gin-gonic/gin" -) - -type Person struct { - Name string `form:"name"` - Address string `form:"address"` -} - -func main() { - route := gin.Default() - route.Any("/testing", startPage) - route.Run(":8085") -} - -func startPage(c *gin.Context) { - var person Person - if c.ShouldBindQuery(&person) == nil { - log.Println("====== Only Bind By Query String ======") - log.Println(person.Name) - log.Println(person.Address) - } - c.String(http.StatusOK, "Success") -} - -``` - -### Bind Query String or Post Data - -See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292). - -```go -package main - -import ( - "log" - "net/http" - "time" - - "github.com/gin-gonic/gin" -) - -type Person struct { - Name string `form:"name"` - Address string `form:"address"` - Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` - CreateTime time.Time `form:"createTime" time_format:"unixNano"` - UnixTime time.Time `form:"unixTime" time_format:"unix"` -} - -func main() { - route := gin.Default() - route.GET("/testing", startPage) - route.Run(":8085") -} - -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#L88 - if c.ShouldBind(&person) == nil { - log.Println(person.Name) - log.Println(person.Address) - log.Println(person.Birthday) - log.Println(person.CreateTime) - log.Println(person.UnixTime) - } - - c.String(http.StatusOK, "Success") -} -``` - -Test it with: - -```sh -curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033" -``` - -### Bind Uri - -See the [detail information](https://github.com/gin-gonic/gin/issues/846). - -```go -package main - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -type Person struct { - ID string `uri:"id" binding:"required,uuid"` - Name string `uri:"name" binding:"required"` -} - -func main() { - route := gin.Default() - route.GET("/:name/:id", func(c *gin.Context) { - var person Person - if err := c.ShouldBindUri(&person); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()}) - return - } - c.JSON(http.StatusOK, gin.H{"name": person.Name, "uuid": person.ID}) - }) - route.Run(":8088") -} -``` - -Test it with: - -```sh -curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3 -curl -v localhost:8088/thinkerou/not-uuid -``` - -### Bind Header - -```go -package main - -import ( - "fmt" - "net/http" - - "github.com/gin-gonic/gin" -) - -type testHeader struct { - Rate int `header:"Rate"` - Domain string `header:"Domain"` -} - -func main() { - r := gin.Default() - r.GET("/", func(c *gin.Context) { - h := testHeader{} - - if err := c.ShouldBindHeader(&h); err != nil { - c.JSON(http.StatusOK, err) - } - - fmt.Printf("%#v\n", h) - c.JSON(http.StatusOK, gin.H{"Rate": h.Rate, "Domain": h.Domain}) - }) - - r.Run() - -// client -// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/ -// output -// {"Domain":"music","Rate":300} -} -``` - -### Bind HTML checkboxes - -See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092) - -main.go - -```go -... - -type myForm struct { - Colors []string `form:"colors[]"` -} - -... - -func formHandler(c *gin.Context) { - var fakeForm myForm - c.ShouldBind(&fakeForm) - c.JSON(http.StatusOK, gin.H{"color": fakeForm.Colors}) -} - -... - -``` - -form.html - -```html -
-

Check some colors

- - - - - - - -
-``` - -result: - -```json -{"color":["red","green","blue"]} -``` - -### Multipart/Urlencoded binding - -```go -type ProfileForm struct { - Name string `form:"name" binding:"required"` - Avatar *multipart.FileHeader `form:"avatar" binding:"required"` - - // or for multiple files - // Avatars []*multipart.FileHeader `form:"avatar" binding:"required"` -} - -func main() { - router := gin.Default() - router.POST("/profile", func(c *gin.Context) { - // you can bind multipart form with explicit binding declaration: - // c.ShouldBindWith(&form, binding.Form) - // or you can simply use autobinding with ShouldBind method: - var form ProfileForm - // in this case proper binding will be automatically selected - if err := c.ShouldBind(&form); err != nil { - c.String(http.StatusBadRequest, "bad request") - return - } - - err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename) - if err != nil { - c.String(http.StatusInternalServerError, "unknown error") - return - } - - // db.Save(&form) - - c.String(http.StatusOK, "ok") - }) - router.Run(":8080") -} -``` - -Test it with: - -```sh -curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile -``` - -### XML, JSON, YAML, TOML and ProtoBuf rendering - -```go -func main() { - r := gin.Default() - - // gin.H is a shortcut for map[string]interface{} - r.GET("/someJSON", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) - }) - - r.GET("/moreJSON", func(c *gin.Context) { - // You also can use a struct - var msg struct { - Name string `json:"user"` - Message string - Number int - } - msg.Name = "Lena" - msg.Message = "hey" - msg.Number = 123 - // Note that msg.Name becomes "user" in the JSON - // Will output : {"user": "Lena", "Message": "hey", "Number": 123} - c.JSON(http.StatusOK, msg) - }) - - r.GET("/someXML", func(c *gin.Context) { - c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) - }) - - r.GET("/someYAML", func(c *gin.Context) { - c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) - }) - - r.GET("/someTOML", func(c *gin.Context) { - c.TOML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) - }) - - r.GET("/someProtoBuf", func(c *gin.Context) { - reps := []int64{int64(1), int64(2)} - label := "test" - // The specific definition of protobuf is written in the testdata/protoexample file. - data := &protoexample.Test{ - Label: &label, - Reps: reps, - } - // Note that data becomes binary data in the response - // Will output protoexample.Test protobuf serialized data - c.ProtoBuf(http.StatusOK, data) - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") -} -``` - -#### SecureJSON - -Using SecureJSON to prevent json hijacking. Default prepends `"while(1),"` to response body if the given struct is array values. - -```go -func main() { - r := gin.Default() - - // You can also use your own secure json prefix - // r.SecureJsonPrefix(")]}',\n") - - r.GET("/someJSON", func(c *gin.Context) { - names := []string{"lena", "austin", "foo"} - - // Will output : while(1);["lena","austin","foo"] - c.SecureJSON(http.StatusOK, names) - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") -} -``` - -#### JSONP - -Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists. - -```go -func main() { - r := gin.Default() - - r.GET("/JSONP", func(c *gin.Context) { - data := gin.H{ - "foo": "bar", - } - - //callback is x - // Will output : x({\"foo\":\"bar\"}) - c.JSONP(http.StatusOK, data) - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") - - // client - // curl http://127.0.0.1:8080/JSONP?callback=x -} -``` - -#### AsciiJSON - -Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII characters. - -```go -func main() { - r := gin.Default() - - r.GET("/someJSON", func(c *gin.Context) { - data := gin.H{ - "lang": "GO语言", - "tag": "
", - } - - // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"} - c.AsciiJSON(http.StatusOK, data) - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") -} -``` - -#### PureJSON - -Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead. -This feature is unavailable in Go 1.6 and lower. - -```go -func main() { - r := gin.Default() - - // Serves unicode entities - r.GET("/json", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "html": "Hello, world!", - }) - }) - - // Serves literal characters - r.GET("/purejson", func(c *gin.Context) { - c.PureJSON(http.StatusOK, gin.H{ - "html": "Hello, world!", - }) - }) - - // listen and serve on 0.0.0.0:8080 - r.Run(":8080") -} -``` - -### Serving static files - -```go -func main() { - router := gin.Default() - router.Static("/assets", "./assets") - router.StaticFS("/more_static", http.Dir("my_file_system")) - router.StaticFile("/favicon.ico", "./resources/favicon.ico") - router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system")) - - // Listen and serve on 0.0.0.0:8080 - router.Run(":8080") -} -``` - -### Serving data from file - -```go -func main() { - router := gin.Default() - - router.GET("/local/file", func(c *gin.Context) { - c.File("local/file.go") - }) - - var fs http.FileSystem = // ... - router.GET("/fs/file", func(c *gin.Context) { - c.FileFromFS("fs/file.go", fs) - }) -} - -``` - -### Serving data from reader - -```go -func main() { - router := gin.Default() - router.GET("/someDataFromReader", func(c *gin.Context) { - response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png") - if err != nil || response.StatusCode != http.StatusOK { - c.Status(http.StatusServiceUnavailable) - return - } - - reader := response.Body - defer reader.Close() - contentLength := response.ContentLength - contentType := response.Header.Get("Content-Type") - - extraHeaders := map[string]string{ - "Content-Disposition": `attachment; filename="gopher.png"`, - } - - c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders) - }) - router.Run(":8080") -} -``` - -### HTML rendering - -Using LoadHTMLGlob() or LoadHTMLFiles() - -```go -func main() { - router := gin.Default() - router.LoadHTMLGlob("templates/*") - //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") - router.GET("/index", func(c *gin.Context) { - c.HTML(http.StatusOK, "index.tmpl", gin.H{ - "title": "Main website", - }) - }) - router.Run(":8080") -} -``` - -templates/index.tmpl - -```html - -

- {{ .title }} -

- -``` - -Using templates with same name in different directories - -```go -func main() { - router := gin.Default() - router.LoadHTMLGlob("templates/**/*") - router.GET("/posts/index", func(c *gin.Context) { - c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ - "title": "Posts", - }) - }) - router.GET("/users/index", func(c *gin.Context) { - c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ - "title": "Users", - }) - }) - router.Run(":8080") -} -``` - -templates/posts/index.tmpl - -```html -{{ define "posts/index.tmpl" }} -

- {{ .title }} -

-

Using posts/index.tmpl

- -{{ end }} -``` - -templates/users/index.tmpl - -```html -{{ define "users/index.tmpl" }} -

- {{ .title }} -

-

Using users/index.tmpl

- -{{ end }} -``` - -#### Custom Template renderer - -You can also use your own html template render - -```go -import "html/template" - -func main() { - router := gin.Default() - html := template.Must(template.ParseFiles("file1", "file2")) - router.SetHTMLTemplate(html) - router.Run(":8080") -} -``` - -#### Custom Delimiters - -You may use custom delims - -```go - r := gin.Default() - r.Delims("{[{", "}]}") - r.LoadHTMLGlob("/path/to/templates") -``` - -#### Custom Template Funcs - -See the detail [example code](https://github.com/gin-gonic/examples/tree/master/template). - -main.go - -```go -import ( - "fmt" - "html/template" - "net/http" - "time" - - "github.com/gin-gonic/gin" -) - -func formatAsDate(t time.Time) string { - year, month, day := t.Date() - return fmt.Sprintf("%d/%02d/%02d", year, month, day) -} - -func main() { - router := gin.Default() - router.Delims("{[{", "}]}") - router.SetFuncMap(template.FuncMap{ - "formatAsDate": formatAsDate, - }) - router.LoadHTMLFiles("./testdata/template/raw.tmpl") - - router.GET("/raw", func(c *gin.Context) { - c.HTML(http.StatusOK, "raw.tmpl", gin.H{ - "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), - }) - }) - - router.Run(":8080") -} - -``` - -raw.tmpl - -```html -Date: {[{.now | formatAsDate}]} -``` - -Result: - -```sh -Date: 2017/07/01 -``` - -### Multitemplate - -Gin allow by default use only one html.Template. Check [a multitemplate render](https://github.com/gin-contrib/multitemplate) for using features like go 1.6 `block template`. - -### Redirects - -Issuing a HTTP redirect is easy. Both internal and external locations are supported. - -```go -r.GET("/test", func(c *gin.Context) { - c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") -}) -``` - -Issuing a HTTP redirect from POST. Refer to issue: [#444](https://github.com/gin-gonic/gin/issues/444) - -```go -r.POST("/test", func(c *gin.Context) { - c.Redirect(http.StatusFound, "/foo") -}) -``` - -Issuing a Router redirect, use `HandleContext` like below. - -``` go -r.GET("/test", func(c *gin.Context) { - c.Request.URL.Path = "/test2" - r.HandleContext(c) -}) -r.GET("/test2", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{"hello": "world"}) -}) -``` - -### Custom Middleware - -```go -func Logger() gin.HandlerFunc { - return func(c *gin.Context) { - t := time.Now() - - // Set example variable - c.Set("example", "12345") - - // before request - - c.Next() - - // after request - latency := time.Since(t) - log.Print(latency) - - // access the status we are sending - status := c.Writer.Status() - log.Println(status) - } -} - -func main() { - r := gin.New() - r.Use(Logger()) - - r.GET("/test", func(c *gin.Context) { - example := c.MustGet("example").(string) - - // it would print: "12345" - log.Println(example) - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") -} -``` - -### Using BasicAuth() middleware - -```go -// simulate some private data -var secrets = gin.H{ - "foo": gin.H{"email": "foo@bar.com", "phone": "123433"}, - "austin": gin.H{"email": "austin@example.com", "phone": "666"}, - "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"}, -} - -func main() { - r := gin.Default() - - // Group using gin.BasicAuth() middleware - // gin.Accounts is a shortcut for map[string]string - authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ - "foo": "bar", - "austin": "1234", - "lena": "hello2", - "manu": "4321", - })) - - // /admin/secrets endpoint - // hit "localhost:8080/admin/secrets - authorized.GET("/secrets", func(c *gin.Context) { - // get user, it was set by the BasicAuth middleware - user := c.MustGet(gin.AuthUserKey).(string) - if secret, ok := secrets[user]; ok { - c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) - } else { - c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("}) - } - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") -} -``` - -### Goroutines inside a middleware - -When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy. - -```go -func main() { - r := gin.Default() - - r.GET("/long_async", func(c *gin.Context) { - // create copy to be used inside the goroutine - cCp := c.Copy() - go func() { - // simulate a long task with time.Sleep(). 5 seconds - time.Sleep(5 * time.Second) - - // note that you are using the copied context "cCp", IMPORTANT - log.Println("Done! in path " + cCp.Request.URL.Path) - }() - }) - - r.GET("/long_sync", func(c *gin.Context) { - // simulate a long task with time.Sleep(). 5 seconds - time.Sleep(5 * time.Second) - - // since we are NOT using a goroutine, we do not have to copy the context - log.Println("Done! in path " + c.Request.URL.Path) - }) - - // Listen and serve on 0.0.0.0:8080 - r.Run(":8080") -} -``` - -### Custom HTTP configuration - -Use `http.ListenAndServe()` directly, like this: - -```go -func main() { - router := gin.Default() - http.ListenAndServe(":8080", router) -} -``` - -or - -```go -func main() { - router := gin.Default() - - s := &http.Server{ - Addr: ":8080", - Handler: router, - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - MaxHeaderBytes: 1 << 20, - } - s.ListenAndServe() -} -``` - -### Support Let's Encrypt - -example for 1-line LetsEncrypt HTTPS servers. - -```go -package main - -import ( - "log" - "net/http" - - "github.com/gin-gonic/autotls" - "github.com/gin-gonic/gin" -) - -func main() { - r := gin.Default() - - // Ping handler - r.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) - - log.Fatal(autotls.Run(r, "example1.com", "example2.com")) -} -``` - -example for custom autocert manager. - -```go -package main - -import ( - "log" - "net/http" - - "github.com/gin-gonic/autotls" - "github.com/gin-gonic/gin" - "golang.org/x/crypto/acme/autocert" -) - -func main() { - r := gin.Default() - - // Ping handler - r.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) - - m := autocert.Manager{ - Prompt: autocert.AcceptTOS, - HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), - Cache: autocert.DirCache("/var/www/.cache"), - } - - log.Fatal(autotls.RunWithManager(r, &m)) -} -``` - -### Run multiple service using Gin - -See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example: - -```go -package main - -import ( - "log" - "net/http" - "time" - - "github.com/gin-gonic/gin" - "golang.org/x/sync/errgroup" -) - -var ( - g errgroup.Group -) - -func router01() http.Handler { - e := gin.New() - e.Use(gin.Recovery()) - e.GET("/", func(c *gin.Context) { - c.JSON( - http.StatusOK, - gin.H{ - "code": http.StatusOK, - "error": "Welcome server 01", - }, - ) - }) - - return e -} - -func router02() http.Handler { - e := gin.New() - e.Use(gin.Recovery()) - e.GET("/", func(c *gin.Context) { - c.JSON( - http.StatusOK, - gin.H{ - "code": http.StatusOK, - "error": "Welcome server 02", - }, - ) - }) - - return e -} - -func main() { - server01 := &http.Server{ - Addr: ":8080", - Handler: router01(), - ReadTimeout: 5 * time.Second, - WriteTimeout: 10 * time.Second, - } - - server02 := &http.Server{ - Addr: ":8081", - Handler: router02(), - ReadTimeout: 5 * time.Second, - WriteTimeout: 10 * time.Second, - } - - g.Go(func() error { - err := server01.ListenAndServe() - if err != nil && err != http.ErrServerClosed { - log.Fatal(err) - } - return err - }) - - g.Go(func() error { - err := server02.ListenAndServe() - if err != nil && err != http.ErrServerClosed { - log.Fatal(err) - } - return err - }) - - if err := g.Wait(); err != nil { - log.Fatal(err) - } -} -``` - -### Graceful shutdown or restart - -There are a few approaches you can use to perform a graceful shutdown or restart. You can make use of third-party packages specifically built for that, or you can manually do the same with the functions and methods from the built-in packages. - -#### Third-party packages - -We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer to issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details. - -```go -router := gin.Default() -router.GET("/", handler) -// [...] -endless.ListenAndServe(":4242", router) -``` - -Alternatives: - -* [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy for Go servers. -* [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server. -* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. - -#### Manually - -In case you are using Go 1.8 or a later version, you may not need to use those libraries. Consider using `http.Server`'s built-in [Shutdown()](https://pkg.go.dev/net/http#Server.Shutdown) method for graceful shutdowns. The example below describes its usage, and we've got more examples using gin [here](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown). - -```go -// +build go1.8 - -package main - -import ( - "context" - "log" - "net/http" - "os" - "os/signal" - "syscall" - "time" - - "github.com/gin-gonic/gin" -) - -func main() { - router := gin.Default() - router.GET("/", func(c *gin.Context) { - time.Sleep(5 * time.Second) - c.String(http.StatusOK, "Welcome Gin Server") - }) - - srv := &http.Server{ - Addr: ":8080", - Handler: router, - } - - // Initializing the server in a goroutine so that - // it won't block the graceful shutdown handling below - go func() { - if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { - log.Printf("listen: %s\n", err) - } - }() - - // Wait for interrupt signal to gracefully shutdown the server with - // a timeout of 5 seconds. - quit := make(chan os.Signal) - // kill (no param) default send syscall.SIGTERM - // kill -2 is syscall.SIGINT - // kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it - signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) - <-quit - log.Println("Shutting down server...") - - // The context is used to inform the server it has 5 seconds to finish - // the request it is currently handling - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - if err := srv.Shutdown(ctx); err != nil { - log.Fatal("Server forced to shutdown:", err) - } - - log.Println("Server exiting") -} -``` - -### Build a single binary with templates - -You can build a server into a single binary containing templates by using the [embed](https://pkg.go.dev/embed) package. - -```go -package main - -import ( - "embed" - "html/template" - "net/http" - - "github.com/gin-gonic/gin" -) - -//go:embed assets/* templates/* -var f embed.FS - -func main() { - router := gin.Default() - templ := template.Must(template.New("").ParseFS(f, "templates/*.tmpl", "templates/foo/*.tmpl")) - router.SetHTMLTemplate(templ) - - // example: /public/assets/images/example.png - router.StaticFS("/public", http.FS(f)) - - router.GET("/", func(c *gin.Context) { - c.HTML(http.StatusOK, "index.tmpl", gin.H{ - "title": "Main website", - }) - }) - - router.GET("/foo", func(c *gin.Context) { - c.HTML(http.StatusOK, "bar.tmpl", gin.H{ - "title": "Foo website", - }) - }) - - router.GET("favicon.ico", func(c *gin.Context) { - file, _ := f.ReadFile("assets/favicon.ico") - c.Data( - http.StatusOK, - "image/x-icon", - file, - ) - }) - - router.Run(":8080") -} -``` - -See a complete example in the `https://github.com/gin-gonic/examples/tree/master/assets-in-binary/example02` directory. - -### Bind form-data request with custom struct - -The follow example using custom struct: - -```go -type StructA struct { - FieldA string `form:"field_a"` -} - -type StructB struct { - NestedStruct StructA - FieldB string `form:"field_b"` -} - -type StructC struct { - NestedStructPointer *StructA - FieldC string `form:"field_c"` -} - -type StructD struct { - NestedAnonyStruct struct { - FieldX string `form:"field_x"` - } - FieldD string `form:"field_d"` -} - -func GetDataB(c *gin.Context) { - var b StructB - c.Bind(&b) - c.JSON(http.StatusOK, gin.H{ - "a": b.NestedStruct, - "b": b.FieldB, - }) -} - -func GetDataC(c *gin.Context) { - var b StructC - c.Bind(&b) - c.JSON(http.StatusOK, gin.H{ - "a": b.NestedStructPointer, - "c": b.FieldC, - }) -} - -func GetDataD(c *gin.Context) { - var b StructD - c.Bind(&b) - c.JSON(http.StatusOK, gin.H{ - "x": b.NestedAnonyStruct, - "d": b.FieldD, - }) -} - -func main() { - r := gin.Default() - r.GET("/getb", GetDataB) - r.GET("/getc", GetDataC) - r.GET("/getd", GetDataD) - - r.Run() -} -``` - -Using the command `curl` command result: - -```sh -$ curl "http://localhost:8080/getb?field_a=hello&field_b=world" -{"a":{"FieldA":"hello"},"b":"world"} -$ curl "http://localhost:8080/getc?field_a=hello&field_c=world" -{"a":{"FieldA":"hello"},"c":"world"} -$ curl "http://localhost:8080/getd?field_x=hello&field_d=world" -{"d":"world","x":{"FieldX":"hello"}} -``` - -### Try to bind body into different structs - -The normal methods for binding request body consumes `c.Request.Body` and they -cannot be called multiple times. - -```go -type formA struct { - Foo string `json:"foo" xml:"foo" binding:"required"` -} - -type formB struct { - Bar string `json:"bar" xml:"bar" binding:"required"` -} - -func SomeHandler(c *gin.Context) { - objA := formA{} - objB := formB{} - // This c.ShouldBind consumes c.Request.Body and it cannot be reused. - if errA := c.ShouldBind(&objA); errA == nil { - c.String(http.StatusOK, `the body should be formA`) - // Always an error is occurred by this because c.Request.Body is EOF now. - } else if errB := c.ShouldBind(&objB); errB == nil { - c.String(http.StatusOK, `the body should be formB`) - } else { - ... - } -} -``` - -For this, you can use `c.ShouldBindBodyWith`. - -```go -func SomeHandler(c *gin.Context) { - objA := formA{} - objB := formB{} - // This reads c.Request.Body and stores the result into the context. - if errA := c.ShouldBindBodyWith(&objA, binding.Form); errA == nil { - c.String(http.StatusOK, `the body should be formA`) - // At this time, it reuses body stored in the context. - } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil { - c.String(http.StatusOK, `the body should be formB JSON`) - // And it can accepts other formats - } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil { - c.String(http.StatusOK, `the body should be formB XML`) - } else { - ... - } -} -``` - -1. `c.ShouldBindBodyWith` stores body into the context before binding. This has -a slight impact to performance, so you should not use this method if you are -enough to call binding at once. -2. This feature is only needed for some formats -- `JSON`, `XML`, `MsgPack`, -`ProtoBuf`. For other formats, `Query`, `Form`, `FormPost`, `FormMultipart`, -can be called by `c.ShouldBind()` multiple times without any damage to -performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)). - -### Bind form-data request with custom struct and custom tag - -```go -const ( - customerTag = "url" - defaultMemory = 32 << 20 -) - -type customerBinding struct {} - -func (customerBinding) Name() string { - return "form" -} - -func (customerBinding) Bind(req *http.Request, obj interface{}) error { - if err := req.ParseForm(); err != nil { - return err - } - if err := req.ParseMultipartForm(defaultMemory); err != nil { - if err != http.ErrNotMultipart { - return err - } - } - if err := binding.MapFormWithTag(obj, req.Form, customerTag); err != nil { - return err - } - return validate(obj) -} - -func validate(obj interface{}) error { - if binding.Validator == nil { - return nil - } - return binding.Validator.ValidateStruct(obj) -} - -// Now we can do this!!! -// FormA is an external type that we can't modify it's tag -type FormA struct { - FieldA string `url:"field_a"` -} - -func ListHandler(s *Service) func(ctx *gin.Context) { - return func(ctx *gin.Context) { - var urlBinding = customerBinding{} - var opt FormA - err := ctx.MustBindWith(&opt, urlBinding) - if err != nil { - ... - } - ... - } -} -``` - -### http2 server push - -http.Pusher is supported only **go1.8+**. See the [golang blog](https://go.dev/blog/h2push) for detail information. - -```go -package main - -import ( - "html/template" - "log" - "net/http" - - "github.com/gin-gonic/gin" -) - -var html = template.Must(template.New("https").Parse(` - - - Https Test - - - -

Welcome, Ginner!

- - -`)) - -func main() { - r := gin.Default() - r.Static("/assets", "./assets") - r.SetHTMLTemplate(html) - - r.GET("/", func(c *gin.Context) { - if pusher := c.Writer.Pusher(); pusher != nil { - // use pusher.Push() to do server push - if err := pusher.Push("/assets/app.js", nil); err != nil { - log.Printf("Failed to push: %v", err) - } - } - c.HTML(http.StatusOK, "https", gin.H{ - "status": "success", - }) - }) - - // Listen and Server in https://127.0.0.1:8080 - r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key") -} -``` - -### Define format for the log of routes - -The default log of routes is: - -```sh -[GIN-debug] POST /foo --> main.main.func1 (3 handlers) -[GIN-debug] GET /bar --> main.main.func2 (3 handlers) -[GIN-debug] GET /status --> main.main.func3 (3 handlers) -``` - -If you want to log this information in given format (e.g. JSON, key values or something else), then you can define this format with `gin.DebugPrintRouteFunc`. -In the example below, we log all routes with standard log package but you can use another log tools that suits of your needs. - -```go -import ( - "log" - "net/http" - - "github.com/gin-gonic/gin" -) - -func main() { - r := gin.Default() - gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { - log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) - } - - r.POST("/foo", func(c *gin.Context) { - c.JSON(http.StatusOK, "foo") - }) - - r.GET("/bar", func(c *gin.Context) { - c.JSON(http.StatusOK, "bar") - }) - - r.GET("/status", func(c *gin.Context) { - c.JSON(http.StatusOK, "ok") - }) - - // Listen and Server in http://0.0.0.0:8080 - r.Run() -} -``` - -### Set and get a cookie - -```go -import ( - "fmt" - - "github.com/gin-gonic/gin" -) - -func main() { - - router := gin.Default() - - router.GET("/cookie", func(c *gin.Context) { - - cookie, err := c.Cookie("gin_cookie") - - if err != nil { - cookie = "NotSet" - c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true) - } - - fmt.Printf("Cookie value: %s \n", cookie) - }) - - router.Run() -} -``` - -## Don't trust all proxies - -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 -specify one of these headers. - -Use function `SetTrustedProxies()` on your `gin.Engine` to specify network addresses -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 -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 -import ( - "fmt" - - "github.com/gin-gonic/gin" -) - -func main() { - - router := gin.Default() - router.SetTrustedProxies([]string{"192.168.1.2"}) - - router.GET("/", func(c *gin.Context) { - // If the client is 192.168.1.2, use the X-Forwarded-For - // header to deduce the original client IP from the trust- - // worthy parts of that header. - // Otherwise, simply return the direct client IP - fmt.Printf("ClientIP: %s\n", c.ClientIP()) - }) - router.Run() -} -``` - -**Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform` -to skip TrustedProxies check, it has a higher priority than TrustedProxies. -Look at the example below: - -```go -import ( - "fmt" - - "github.com/gin-gonic/gin" -) - -func main() { - - router := gin.Default() - // Use predefined header gin.PlatformXXX - router.TrustedPlatform = gin.PlatformGoogleAppEngine - // Or set your own trusted request header for another trusted proxy service - // Don't set it to any suspect request header, it's unsafe - router.TrustedPlatform = "X-CDN-IP" - - router.GET("/", func(c *gin.Context) { - // If you set TrustedPlatform, ClientIP() will resolve the - // corresponding header and return IP directly - fmt.Printf("ClientIP: %s\n", c.ClientIP()) - }) - router.Run() -} -``` - -## Testing - -The `net/http/httptest` package is preferable way for HTTP testing. - -```go -package main - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -func setupRouter() *gin.Engine { - r := gin.Default() - r.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) - return r -} - -func main() { - r := setupRouter() - r.Run(":8080") -} -``` - -Test for code example above: - -```go -package main - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPingRoute(t *testing.T) { - router := setupRouter() - - w := httptest.NewRecorder() - req, _ := http.NewRequest(http.MethodGet, "/ping", nil) - router.ServeHTTP(w, req) - - assert.Equal(t, http.StatusOK, w.Code) - assert.Equal(t, "pong", w.Body.String()) -} -``` - -## Users - -Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. - -* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. -* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform. -* [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. -* [lura](https://github.com/luraproject/lura): Ultra performant API Gateway with middlewares. -* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. -* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes. -* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. +Please see [CONTRIBUTING](CONTRIBUTING.md) for details on submitting patches and the contribution workflow. \ No newline at end of file diff --git a/docs/doc.md b/docs/doc.md new file mode 100644 index 0000000000..008a91db78 --- /dev/null +++ b/docs/doc.md @@ -0,0 +1,2246 @@ +# Gin Quick Start + +## Contents + +- [Build Tags](#build-tags) + - [Build with json replacement](#build-with-json-replacement) + - [Build without `MsgPack` rendering feature](#build-without-msgpack-rendering-feature) +- [API Examples](#api-examples) + - [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options) + - [Parameters in path](#parameters-in-path) + - [Querystring parameters](#querystring-parameters) + - [Multipart/Urlencoded Form](#multiparturlencoded-form) + - [Another example: query + post form](#another-example-query--post-form) + - [Map as querystring or postform parameters](#map-as-querystring-or-postform-parameters) + - [Upload files](#upload-files) + - [Single file](#single-file) + - [Multiple files](#multiple-files) + - [Grouping routes](#grouping-routes) + - [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default) + - [Using middleware](#using-middleware) + - [Custom Recovery behavior](#custom-recovery-behavior) + - [How to write log file](#how-to-write-log-file) + - [Custom Log Format](#custom-log-format) + - [Controlling Log output coloring](#controlling-log-output-coloring) + - [Model binding and validation](#model-binding-and-validation) + - [Custom Validators](#custom-validators) + - [Only Bind Query String](#only-bind-query-string) + - [Bind Query String or Post Data](#bind-query-string-or-post-data) + - [Bind Uri](#bind-uri) + - [Bind Header](#bind-header) + - [Bind HTML checkboxes](#bind-html-checkboxes) + - [Multipart/Urlencoded binding](#multiparturlencoded-binding) + - [XML, JSON, YAML, TOML and ProtoBuf rendering](#xml-json-yaml-toml-and-protobuf-rendering) + - [SecureJSON](#securejson) + - [JSONP](#jsonp) + - [AsciiJSON](#asciijson) + - [PureJSON](#purejson) + - [Serving static files](#serving-static-files) + - [Serving data from file](#serving-data-from-file) + - [Serving data from reader](#serving-data-from-reader) + - [HTML rendering](#html-rendering) + - [Custom Template renderer](#custom-template-renderer) + - [Custom Delimiters](#custom-delimiters) + - [Custom Template Funcs](#custom-template-funcs) + - [Multitemplate](#multitemplate) + - [Redirects](#redirects) + - [Custom Middleware](#custom-middleware) + - [Using BasicAuth() middleware](#using-basicauth-middleware) + - [Goroutines inside a middleware](#goroutines-inside-a-middleware) + - [Custom HTTP configuration](#custom-http-configuration) + - [Support Let's Encrypt](#support-lets-encrypt) + - [Run multiple service using Gin](#run-multiple-service-using-gin) + - [Graceful shutdown or restart](#graceful-shutdown-or-restart) + - [Third-party packages](#third-party-packages) + - [Manually](#manually) + - [Build a single binary with templates](#build-a-single-binary-with-templates) + - [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct) + - [Try to bind body into different structs](#try-to-bind-body-into-different-structs) + - [Bind form-data request with custom struct and custom tag](#bind-form-data-request-with-custom-struct-and-custom-tag) + - [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](#dont-trust-all-proxies) +- [Testing](#testing) + +## Build tags + +### Build with json replacement + +Gin uses `encoding/json` as default json package but you can change it by build from other tags. + +[jsoniter](https://github.com/json-iterator/go) + +```sh +go build -tags=jsoniter . +``` + +[go-json](https://github.com/goccy/go-json) + +```sh +go build -tags=go_json . +``` + +[sonic](https://github.com/bytedance/sonic) (you have to ensure that your cpu support avx instruction.) + +```sh +$ go build -tags="sonic avx" . +``` + +### Build without `MsgPack` rendering feature + +Gin enables `MsgPack` rendering feature by default. But you can disable this feature by specifying `nomsgpack` build tag. + +```sh +go build -tags=nomsgpack . +``` + +This is useful to reduce the binary size of executable files. See the [detail information](https://github.com/gin-gonic/gin/pull/1852). + +## API Examples + +You can find a number of ready-to-run examples at [Gin examples repository](https://github.com/gin-gonic/examples). + +### Using GET, POST, PUT, PATCH, DELETE and OPTIONS + +```go +func main() { + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + router.GET("/someGet", getting) + router.POST("/somePost", posting) + router.PUT("/somePut", putting) + router.DELETE("/someDelete", deleting) + router.PATCH("/somePatch", patching) + router.HEAD("/someHead", head) + router.OPTIONS("/someOptions", options) + + // By default it serves on :8080 unless a + // PORT environment variable was defined. + router.Run() + // router.Run(":3000") for a hard coded port +} +``` + +### Parameters in path + +```go +func main() { + router := gin.Default() + + // This handler will match /user/john but will not match /user/ or /user + router.GET("/user/:name", func(c *gin.Context) { + name := c.Param("name") + c.String(http.StatusOK, "Hello %s", name) + }) + + // However, this one will match /user/john/ and also /user/john/send + // If no other routers match /user/john, it will redirect to /user/john/ + router.GET("/user/:name/*action", func(c *gin.Context) { + name := c.Param("name") + action := c.Param("action") + message := name + " is " + action + c.String(http.StatusOK, message) + }) + + // For each matched request Context will hold the route definition + router.POST("/user/:name/*action", func(c *gin.Context) { + b := c.FullPath() == "/user/:name/*action" // true + c.String(http.StatusOK, "%t", b) + }) + + // This handler will add a new router for /user/groups. + // Exact routes are resolved before param routes, regardless of the order they were defined. + // Routes starting with /user/groups are never interpreted as /user/:name/... routes + router.GET("/user/groups", func(c *gin.Context) { + c.String(http.StatusOK, "The available groups are [...]") + }) + + router.Run(":8080") +} +``` + +### Querystring parameters + +```go +func main() { + router := gin.Default() + + // Query string parameters are parsed using the existing underlying request object. + // The request responds to an url matching: /welcome?firstname=Jane&lastname=Doe + router.GET("/welcome", func(c *gin.Context) { + firstname := c.DefaultQuery("firstname", "Guest") + lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") + + c.String(http.StatusOK, "Hello %s %s", firstname, lastname) + }) + router.Run(":8080") +} +``` + +### Multipart/Urlencoded Form + +```go +func main() { + router := gin.Default() + + router.POST("/form_post", func(c *gin.Context) { + message := c.PostForm("message") + nick := c.DefaultPostForm("nick", "anonymous") + + c.JSON(http.StatusOK, gin.H{ + "status": "posted", + "message": message, + "nick": nick, + }) + }) + router.Run(":8080") +} +``` + +### Another example: query + post form + +```sh +POST /post?id=1234&page=1 HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +name=manu&message=this_is_great +``` + +```go +func main() { + router := gin.Default() + + router.POST("/post", func(c *gin.Context) { + + id := c.Query("id") + page := c.DefaultQuery("page", "0") + name := c.PostForm("name") + message := c.PostForm("message") + + fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) + }) + router.Run(":8080") +} +``` + +```sh +id: 1234; page: 1; name: manu; message: this_is_great +``` + +### Map as querystring or postform parameters + +```sh +POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +names[first]=thinkerou&names[second]=tianou +``` + +```go +func main() { + router := gin.Default() + + router.POST("/post", func(c *gin.Context) { + + ids := c.QueryMap("ids") + names := c.PostFormMap("names") + + fmt.Printf("ids: %v; names: %v", ids, names) + }) + router.Run(":8080") +} +``` + +```sh +ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou] +``` + +### Upload files + +#### Single file + +References issue [#774](https://github.com/gin-gonic/gin/issues/774) and detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/single). + +`file.Filename` **SHOULD NOT** be trusted. See [`Content-Disposition` on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Directives) and [#1693](https://github.com/gin-gonic/gin/issues/1693) + +> The filename is always optional and must not be used blindly by the application: path information should be stripped, and conversion to the server file system rules should be done. + +```go +func main() { + router := gin.Default() + // 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 + file, _ := c.FormFile("file") + log.Println(file.Filename) + + // Upload the file to specific dst. + c.SaveUploadedFile(file, dst) + + c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) + }) + router.Run(":8080") +} +``` + +How to `curl`: + +```bash +curl -X POST http://localhost:8080/upload \ + -F "file=@/Users/appleboy/test.zip" \ + -H "Content-Type: multipart/form-data" +``` + +#### Multiple files + +See the detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/multiple). + +```go +func main() { + router := gin.Default() + // 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) { + // Multipart form + form, _ := c.MultipartForm() + files := form.File["upload[]"] + + for _, file := range files { + log.Println(file.Filename) + + // Upload the file to specific dst. + c.SaveUploadedFile(file, dst) + } + c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) + }) + router.Run(":8080") +} +``` + +How to `curl`: + +```bash +curl -X POST http://localhost:8080/upload \ + -F "upload[]=@/Users/appleboy/test1.zip" \ + -F "upload[]=@/Users/appleboy/test2.zip" \ + -H "Content-Type: multipart/form-data" +``` + +### Grouping routes + +```go +func main() { + router := gin.Default() + + // Simple group: v1 + v1 := router.Group("/v1") + { + v1.POST("/login", loginEndpoint) + v1.POST("/submit", submitEndpoint) + v1.POST("/read", readEndpoint) + } + + // Simple group: v2 + v2 := router.Group("/v2") + { + v2.POST("/login", loginEndpoint) + v2.POST("/submit", submitEndpoint) + v2.POST("/read", readEndpoint) + } + + router.Run(":8080") +} +``` + +### Blank Gin without middleware by default + +Use + +```go +r := gin.New() +``` + +instead of + +```go +// Default With the Logger and Recovery middleware already attached +r := gin.Default() +``` + +### Using middleware + +```go +func main() { + // Creates a router without any middleware by default + r := gin.New() + + // Global middleware + // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. + // By default gin.DefaultWriter = os.Stdout + r.Use(gin.Logger()) + + // Recovery middleware recovers from any panics and writes a 500 if there was one. + r.Use(gin.Recovery()) + + // Per route middleware, you can add as many as you desire. + r.GET("/benchmark", MyBenchLogger(), benchEndpoint) + + // Authorization group + // authorized := r.Group("/", AuthRequired()) + // exactly the same as: + authorized := r.Group("/") + // per group middleware! in this case we use the custom created + // AuthRequired() middleware just in the "authorized" group. + authorized.Use(AuthRequired()) + { + authorized.POST("/login", loginEndpoint) + authorized.POST("/submit", submitEndpoint) + authorized.POST("/read", readEndpoint) + + // nested group + testing := authorized.Group("testing") + // visit 0.0.0.0:8080/testing/analytics + testing.GET("/analytics", analyticsEndpoint) + } + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Custom Recovery behavior + +```go +func main() { + // Creates a router without any middleware by default + r := gin.New() + + // Global middleware + // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. + // By default gin.DefaultWriter = os.Stdout + r.Use(gin.Logger()) + + // Recovery middleware recovers from any panics and writes a 500 if there was one. + r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) { + if err, ok := recovered.(string); ok { + c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) + } + c.AbortWithStatus(http.StatusInternalServerError) + })) + + r.GET("/panic", func(c *gin.Context) { + // panic with a string -- the custom middleware could save this to a database or report it to the user + panic("foo") + }) + + r.GET("/", func(c *gin.Context) { + c.String(http.StatusOK, "ohai") + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### How to write log file + +```go +func main() { + // Disable Console Color, you don't need console color when writing the logs to file. + gin.DisableConsoleColor() + + // Logging to a file. + f, _ := os.Create("gin.log") + gin.DefaultWriter = io.MultiWriter(f) + + // Use the following code if you need to write the logs to file and console at the same time. + // gin.DefaultWriter = io.MultiWriter(f, os.Stdout) + + router := gin.Default() + router.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) + +    router.Run(":8080") +} +``` + +### Custom Log Format + +```go +func main() { + router := gin.New() + + // LoggerWithFormatter middleware will write the logs to gin.DefaultWriter + // By default gin.DefaultWriter = os.Stdout + router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { + + // your custom format + return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", + param.ClientIP, + param.TimeStamp.Format(time.RFC1123), + param.Method, + param.Path, + param.Request.Proto, + param.StatusCode, + param.Latency, + param.Request.UserAgent(), + param.ErrorMessage, + ) + })) + router.Use(gin.Recovery()) + + router.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) + + router.Run(":8080") +} +``` + +Sample Output + +```sh +::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" " +``` + +### Controlling Log output coloring + +By default, logs output on console should be colorized depending on the detected TTY. + +Never colorize logs: + +```go +func main() { + // Disable log's color + gin.DisableConsoleColor() + + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + router.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) + + router.Run(":8080") +} +``` + +Always colorize logs: + +```go +func main() { + // Force log's color + gin.ForceConsoleColor() + + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + router.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) + + router.Run(":8080") +} +``` + +### Model binding and validation + +To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML, TOML and standard form values (foo=bar&boo=baz). + +Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://pkg.go.dev/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags). + +Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`. + +Also, Gin provides two sets of methods for binding: + +- **Type** - Must bind + - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader`, `BindTOML` + - **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method. +- **Type** - Should bind + - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader`, `ShouldBindTOML`, + - **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately. + +When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`. + +You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has an empty value when binding, an error will be returned. + +```go +// Binding from JSON +type Login struct { + User string `form:"user" json:"user" xml:"user" binding:"required"` + Password string `form:"password" json:"password" xml:"password" binding:"required"` +} + +func main() { + router := gin.Default() + + // Example for binding JSON ({"user": "manu", "password": "123"}) + router.POST("/loginJSON", func(c *gin.Context) { + var json Login + if err := c.ShouldBindJSON(&json); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if json.User != "manu" || json.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Example for binding XML ( + // + // + // manu + // 123 + // ) + router.POST("/loginXML", func(c *gin.Context) { + var xml Login + if err := c.ShouldBindXML(&xml); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if xml.User != "manu" || xml.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Example for binding a HTML form (user=manu&password=123) + router.POST("/loginForm", func(c *gin.Context) { + var form Login + // This will infer what binder to use depending on the content-type header. + if err := c.ShouldBind(&form); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if form.User != "manu" || form.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Listen and serve on 0.0.0.0:8080 + router.Run(":8080") +} +``` + +Sample request + +```sh +$ curl -v -X POST \ + http://localhost:8080/loginJSON \ + -H 'content-type: application/json' \ + -d '{ "user": "manu" }' +> POST /loginJSON HTTP/1.1 +> Host: localhost:8080 +> User-Agent: curl/7.51.0 +> Accept: */* +> content-type: application/json +> Content-Length: 18 +> +* upload completely sent off: 18 out of 18 bytes +< HTTP/1.1 400 Bad Request +< Content-Type: application/json; charset=utf-8 +< Date: Fri, 04 Aug 2017 03:51:31 GMT +< Content-Length: 100 +< +{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"} +``` + +Skip validate: when running the above example using the above the `curl` command, it returns error. Because the example use `binding:"required"` for `Password`. If use `binding:"-"` for `Password`, then it will not return error when running the above example again. + +### Custom Validators + +It is also possible to register custom validators. See the [example code](https://github.com/gin-gonic/examples/tree/master/custom-validation/server.go). + +```go +package main + +import ( + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" +) + +// Booking contains binded and validated data. +type Booking struct { + CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` + CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"` +} + +var bookableDate validator.Func = func(fl validator.FieldLevel) bool { + date, ok := fl.Field().Interface().(time.Time) + if ok { + today := time.Now() + if today.After(date) { + return false + } + } + return true +} + +func main() { + route := gin.Default() + + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + v.RegisterValidation("bookabledate", bookableDate) + } + + route.GET("/bookable", getBookable) + route.Run(":8085") +} + +func getBookable(c *gin.Context) { + var b Booking + if err := c.ShouldBindWith(&b, binding.Query); err == nil { + c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"}) + } else { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + } +} +``` + +```console +$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17" +{"message":"Booking dates are valid!"} + +$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09" +{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"} + +$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10" +{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}% +``` + +[Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registered this way. +See the [struct-lvl-validation example](https://github.com/gin-gonic/examples/tree/master/struct-lvl-validations) to learn more. + +### Only Bind Query String + +`ShouldBindQuery` function only binds the query params and not the post data. See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017). + +```go +package main + +import ( + "log" + "net/http" + + "github.com/gin-gonic/gin" +) + +type Person struct { + Name string `form:"name"` + Address string `form:"address"` +} + +func main() { + route := gin.Default() + route.Any("/testing", startPage) + route.Run(":8085") +} + +func startPage(c *gin.Context) { + var person Person + if c.ShouldBindQuery(&person) == nil { + log.Println("====== Only Bind By Query String ======") + log.Println(person.Name) + log.Println(person.Address) + } + c.String(http.StatusOK, "Success") +} + +``` + +### Bind Query String or Post Data + +See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292). + +```go +package main + +import ( + "log" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +type Person struct { + Name string `form:"name"` + Address string `form:"address"` + Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` + CreateTime time.Time `form:"createTime" time_format:"unixNano"` + UnixTime time.Time `form:"unixTime" time_format:"unix"` +} + +func main() { + route := gin.Default() + route.GET("/testing", startPage) + route.Run(":8085") +} + +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#L88 + if c.ShouldBind(&person) == nil { + log.Println(person.Name) + log.Println(person.Address) + log.Println(person.Birthday) + log.Println(person.CreateTime) + log.Println(person.UnixTime) + } + + c.String(http.StatusOK, "Success") +} +``` + +Test it with: + +```sh +curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033" +``` + +### Bind Uri + +See the [detail information](https://github.com/gin-gonic/gin/issues/846). + +```go +package main + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type Person struct { + ID string `uri:"id" binding:"required,uuid"` + Name string `uri:"name" binding:"required"` +} + +func main() { + route := gin.Default() + route.GET("/:name/:id", func(c *gin.Context) { + var person Person + if err := c.ShouldBindUri(&person); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"name": person.Name, "uuid": person.ID}) + }) + route.Run(":8088") +} +``` + +Test it with: + +```sh +curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3 +curl -v localhost:8088/thinkerou/not-uuid +``` + +### Bind Header + +```go +package main + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" +) + +type testHeader struct { + Rate int `header:"Rate"` + Domain string `header:"Domain"` +} + +func main() { + r := gin.Default() + r.GET("/", func(c *gin.Context) { + h := testHeader{} + + if err := c.ShouldBindHeader(&h); err != nil { + c.JSON(http.StatusOK, err) + } + + fmt.Printf("%#v\n", h) + c.JSON(http.StatusOK, gin.H{"Rate": h.Rate, "Domain": h.Domain}) + }) + + r.Run() + +// client +// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/ +// output +// {"Domain":"music","Rate":300} +} +``` + +### Bind HTML checkboxes + +See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092) + +main.go + +```go +... + +type myForm struct { + Colors []string `form:"colors[]"` +} + +... + +func formHandler(c *gin.Context) { + var fakeForm myForm + c.ShouldBind(&fakeForm) + c.JSON(http.StatusOK, gin.H{"color": fakeForm.Colors}) +} + +... + +``` + +form.html + +```html +
+

Check some colors

+ + + + + + + +
+``` + +result: + +```json +{"color":["red","green","blue"]} +``` + +### Multipart/Urlencoded binding + +```go +type ProfileForm struct { + Name string `form:"name" binding:"required"` + Avatar *multipart.FileHeader `form:"avatar" binding:"required"` + + // or for multiple files + // Avatars []*multipart.FileHeader `form:"avatar" binding:"required"` +} + +func main() { + router := gin.Default() + router.POST("/profile", func(c *gin.Context) { + // you can bind multipart form with explicit binding declaration: + // c.ShouldBindWith(&form, binding.Form) + // or you can simply use autobinding with ShouldBind method: + var form ProfileForm + // in this case proper binding will be automatically selected + if err := c.ShouldBind(&form); err != nil { + c.String(http.StatusBadRequest, "bad request") + return + } + + err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename) + if err != nil { + c.String(http.StatusInternalServerError, "unknown error") + return + } + + // db.Save(&form) + + c.String(http.StatusOK, "ok") + }) + router.Run(":8080") +} +``` + +Test it with: + +```sh +curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile +``` + +### XML, JSON, YAML, TOML and ProtoBuf rendering + +```go +func main() { + r := gin.Default() + + // gin.H is a shortcut for map[string]interface{} + r.GET("/someJSON", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/moreJSON", func(c *gin.Context) { + // You also can use a struct + var msg struct { + Name string `json:"user"` + Message string + Number int + } + msg.Name = "Lena" + msg.Message = "hey" + msg.Number = 123 + // Note that msg.Name becomes "user" in the JSON + // Will output : {"user": "Lena", "Message": "hey", "Number": 123} + c.JSON(http.StatusOK, msg) + }) + + r.GET("/someXML", func(c *gin.Context) { + c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/someYAML", func(c *gin.Context) { + c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/someTOML", func(c *gin.Context) { + c.TOML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/someProtoBuf", func(c *gin.Context) { + reps := []int64{int64(1), int64(2)} + label := "test" + // The specific definition of protobuf is written in the testdata/protoexample file. + data := &protoexample.Test{ + Label: &label, + Reps: reps, + } + // Note that data becomes binary data in the response + // Will output protoexample.Test protobuf serialized data + c.ProtoBuf(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +#### SecureJSON + +Using SecureJSON to prevent json hijacking. Default prepends `"while(1),"` to response body if the given struct is array values. + +```go +func main() { + r := gin.Default() + + // You can also use your own secure json prefix + // r.SecureJsonPrefix(")]}',\n") + + r.GET("/someJSON", func(c *gin.Context) { + names := []string{"lena", "austin", "foo"} + + // Will output : while(1);["lena","austin","foo"] + c.SecureJSON(http.StatusOK, names) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +#### JSONP + +Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists. + +```go +func main() { + r := gin.Default() + + r.GET("/JSONP", func(c *gin.Context) { + data := gin.H{ + "foo": "bar", + } + + //callback is x + // Will output : x({\"foo\":\"bar\"}) + c.JSONP(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") + + // client + // curl http://127.0.0.1:8080/JSONP?callback=x +} +``` + +#### AsciiJSON + +Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII characters. + +```go +func main() { + r := gin.Default() + + r.GET("/someJSON", func(c *gin.Context) { + data := gin.H{ + "lang": "GO语言", + "tag": "
", + } + + // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"} + c.AsciiJSON(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +#### PureJSON + +Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead. +This feature is unavailable in Go 1.6 and lower. + +```go +func main() { + r := gin.Default() + + // Serves unicode entities + r.GET("/json", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "html": "Hello, world!", + }) + }) + + // Serves literal characters + r.GET("/purejson", func(c *gin.Context) { + c.PureJSON(http.StatusOK, gin.H{ + "html": "Hello, world!", + }) + }) + + // listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Serving static files + +```go +func main() { + router := gin.Default() + router.Static("/assets", "./assets") + router.StaticFS("/more_static", http.Dir("my_file_system")) + router.StaticFile("/favicon.ico", "./resources/favicon.ico") + router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system")) + + // Listen and serve on 0.0.0.0:8080 + router.Run(":8080") +} +``` + +### Serving data from file + +```go +func main() { + router := gin.Default() + + router.GET("/local/file", func(c *gin.Context) { + c.File("local/file.go") + }) + + var fs http.FileSystem = // ... + router.GET("/fs/file", func(c *gin.Context) { + c.FileFromFS("fs/file.go", fs) + }) +} + +``` + +### Serving data from reader + +```go +func main() { + router := gin.Default() + router.GET("/someDataFromReader", func(c *gin.Context) { + response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png") + if err != nil || response.StatusCode != http.StatusOK { + c.Status(http.StatusServiceUnavailable) + return + } + + reader := response.Body + defer reader.Close() + contentLength := response.ContentLength + contentType := response.Header.Get("Content-Type") + + extraHeaders := map[string]string{ + "Content-Disposition": `attachment; filename="gopher.png"`, + } + + c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders) + }) + router.Run(":8080") +} +``` + +### HTML rendering + +Using LoadHTMLGlob() or LoadHTMLFiles() + +```go +func main() { + router := gin.Default() + router.LoadHTMLGlob("templates/*") + //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") + router.GET("/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "index.tmpl", gin.H{ + "title": "Main website", + }) + }) + router.Run(":8080") +} +``` + +templates/index.tmpl + +```html + +

+ {{ .title }} +

+ +``` + +Using templates with same name in different directories + +```go +func main() { + router := gin.Default() + router.LoadHTMLGlob("templates/**/*") + router.GET("/posts/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ + "title": "Posts", + }) + }) + router.GET("/users/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ + "title": "Users", + }) + }) + router.Run(":8080") +} +``` + +templates/posts/index.tmpl + +```html +{{ define "posts/index.tmpl" }} +

+ {{ .title }} +

+

Using posts/index.tmpl

+ +{{ end }} +``` + +templates/users/index.tmpl + +```html +{{ define "users/index.tmpl" }} +

+ {{ .title }} +

+

Using users/index.tmpl

+ +{{ end }} +``` + +#### Custom Template renderer + +You can also use your own html template render + +```go +import "html/template" + +func main() { + router := gin.Default() + html := template.Must(template.ParseFiles("file1", "file2")) + router.SetHTMLTemplate(html) + router.Run(":8080") +} +``` + +#### Custom Delimiters + +You may use custom delims + +```go + r := gin.Default() + r.Delims("{[{", "}]}") + r.LoadHTMLGlob("/path/to/templates") +``` + +#### Custom Template Funcs + +See the detail [example code](https://github.com/gin-gonic/examples/tree/master/template). + +main.go + +```go +import ( + "fmt" + "html/template" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +func formatAsDate(t time.Time) string { + year, month, day := t.Date() + return fmt.Sprintf("%d/%02d/%02d", year, month, day) +} + +func main() { + router := gin.Default() + router.Delims("{[{", "}]}") + router.SetFuncMap(template.FuncMap{ + "formatAsDate": formatAsDate, + }) + router.LoadHTMLFiles("./testdata/template/raw.tmpl") + + router.GET("/raw", func(c *gin.Context) { + c.HTML(http.StatusOK, "raw.tmpl", gin.H{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), + }) + }) + + router.Run(":8080") +} + +``` + +raw.tmpl + +```html +Date: {[{.now | formatAsDate}]} +``` + +Result: + +```sh +Date: 2017/07/01 +``` + +### Multitemplate + +Gin allow by default use only one html.Template. Check [a multitemplate render](https://github.com/gin-contrib/multitemplate) for using features like go 1.6 `block template`. + +### Redirects + +Issuing a HTTP redirect is easy. Both internal and external locations are supported. + +```go +r.GET("/test", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") +}) +``` + +Issuing a HTTP redirect from POST. Refer to issue: [#444](https://github.com/gin-gonic/gin/issues/444) + +```go +r.POST("/test", func(c *gin.Context) { + c.Redirect(http.StatusFound, "/foo") +}) +``` + +Issuing a Router redirect, use `HandleContext` like below. + +``` go +r.GET("/test", func(c *gin.Context) { + c.Request.URL.Path = "/test2" + r.HandleContext(c) +}) +r.GET("/test2", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"hello": "world"}) +}) +``` + +### Custom Middleware + +```go +func Logger() gin.HandlerFunc { + return func(c *gin.Context) { + t := time.Now() + + // Set example variable + c.Set("example", "12345") + + // before request + + c.Next() + + // after request + latency := time.Since(t) + log.Print(latency) + + // access the status we are sending + status := c.Writer.Status() + log.Println(status) + } +} + +func main() { + r := gin.New() + r.Use(Logger()) + + r.GET("/test", func(c *gin.Context) { + example := c.MustGet("example").(string) + + // it would print: "12345" + log.Println(example) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Using BasicAuth() middleware + +```go +// simulate some private data +var secrets = gin.H{ + "foo": gin.H{"email": "foo@bar.com", "phone": "123433"}, + "austin": gin.H{"email": "austin@example.com", "phone": "666"}, + "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"}, +} + +func main() { + r := gin.Default() + + // Group using gin.BasicAuth() middleware + // gin.Accounts is a shortcut for map[string]string + authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ + "foo": "bar", + "austin": "1234", + "lena": "hello2", + "manu": "4321", + })) + + // /admin/secrets endpoint + // hit "localhost:8080/admin/secrets + authorized.GET("/secrets", func(c *gin.Context) { + // get user, it was set by the BasicAuth middleware + user := c.MustGet(gin.AuthUserKey).(string) + if secret, ok := secrets[user]; ok { + c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) + } else { + c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("}) + } + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Goroutines inside a middleware + +When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy. + +```go +func main() { + r := gin.Default() + + r.GET("/long_async", func(c *gin.Context) { + // create copy to be used inside the goroutine + cCp := c.Copy() + go func() { + // simulate a long task with time.Sleep(). 5 seconds + time.Sleep(5 * time.Second) + + // note that you are using the copied context "cCp", IMPORTANT + log.Println("Done! in path " + cCp.Request.URL.Path) + }() + }) + + r.GET("/long_sync", func(c *gin.Context) { + // simulate a long task with time.Sleep(). 5 seconds + time.Sleep(5 * time.Second) + + // since we are NOT using a goroutine, we do not have to copy the context + log.Println("Done! in path " + c.Request.URL.Path) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Custom HTTP configuration + +Use `http.ListenAndServe()` directly, like this: + +```go +func main() { + router := gin.Default() + http.ListenAndServe(":8080", router) +} +``` + +or + +```go +func main() { + router := gin.Default() + + s := &http.Server{ + Addr: ":8080", + Handler: router, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + s.ListenAndServe() +} +``` + +### Support Let's Encrypt + +example for 1-line LetsEncrypt HTTPS servers. + +```go +package main + +import ( + "log" + "net/http" + + "github.com/gin-gonic/autotls" + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + + // Ping handler + r.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) + + log.Fatal(autotls.Run(r, "example1.com", "example2.com")) +} +``` + +example for custom autocert manager. + +```go +package main + +import ( + "log" + "net/http" + + "github.com/gin-gonic/autotls" + "github.com/gin-gonic/gin" + "golang.org/x/crypto/acme/autocert" +) + +func main() { + r := gin.Default() + + // Ping handler + r.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) + + m := autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), + Cache: autocert.DirCache("/var/www/.cache"), + } + + log.Fatal(autotls.RunWithManager(r, &m)) +} +``` + +### Run multiple service using Gin + +See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example: + +```go +package main + +import ( + "log" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "golang.org/x/sync/errgroup" +) + +var ( + g errgroup.Group +) + +func router01() http.Handler { + e := gin.New() + e.Use(gin.Recovery()) + e.GET("/", func(c *gin.Context) { + c.JSON( + http.StatusOK, + gin.H{ + "code": http.StatusOK, + "error": "Welcome server 01", + }, + ) + }) + + return e +} + +func router02() http.Handler { + e := gin.New() + e.Use(gin.Recovery()) + e.GET("/", func(c *gin.Context) { + c.JSON( + http.StatusOK, + gin.H{ + "code": http.StatusOK, + "error": "Welcome server 02", + }, + ) + }) + + return e +} + +func main() { + server01 := &http.Server{ + Addr: ":8080", + Handler: router01(), + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + server02 := &http.Server{ + Addr: ":8081", + Handler: router02(), + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + g.Go(func() error { + err := server01.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatal(err) + } + return err + }) + + g.Go(func() error { + err := server02.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatal(err) + } + return err + }) + + if err := g.Wait(); err != nil { + log.Fatal(err) + } +} +``` + +### Graceful shutdown or restart + +There are a few approaches you can use to perform a graceful shutdown or restart. You can make use of third-party packages specifically built for that, or you can manually do the same with the functions and methods from the built-in packages. + +#### Third-party packages + +We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer to issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details. + +```go +router := gin.Default() +router.GET("/", handler) +// [...] +endless.ListenAndServe(":4242", router) +``` + +Alternatives: + +* [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy for Go servers. +* [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server. +* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. + +#### Manually + +In case you are using Go 1.8 or a later version, you may not need to use those libraries. Consider using `http.Server`'s built-in [Shutdown()](https://pkg.go.dev/net/http#Server.Shutdown) method for graceful shutdowns. The example below describes its usage, and we've got more examples using gin [here](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown). + +```go +// +build go1.8 + +package main + +import ( + "context" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/gin-gonic/gin" +) + +func main() { + router := gin.Default() + router.GET("/", func(c *gin.Context) { + time.Sleep(5 * time.Second) + c.String(http.StatusOK, "Welcome Gin Server") + }) + + srv := &http.Server{ + Addr: ":8080", + Handler: router, + } + + // Initializing the server in a goroutine so that + // it won't block the graceful shutdown handling below + go func() { + if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Printf("listen: %s\n", err) + } + }() + + // Wait for interrupt signal to gracefully shutdown the server with + // a timeout of 5 seconds. + quit := make(chan os.Signal) + // kill (no param) default send syscall.SIGTERM + // kill -2 is syscall.SIGINT + // kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + log.Println("Shutting down server...") + + // The context is used to inform the server it has 5 seconds to finish + // the request it is currently handling + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := srv.Shutdown(ctx); err != nil { + log.Fatal("Server forced to shutdown:", err) + } + + log.Println("Server exiting") +} +``` + +### Build a single binary with templates + +You can build a server into a single binary containing templates by using the [embed](https://pkg.go.dev/embed) package. + +```go +package main + +import ( + "embed" + "html/template" + "net/http" + + "github.com/gin-gonic/gin" +) + +//go:embed assets/* templates/* +var f embed.FS + +func main() { + router := gin.Default() + templ := template.Must(template.New("").ParseFS(f, "templates/*.tmpl", "templates/foo/*.tmpl")) + router.SetHTMLTemplate(templ) + + // example: /public/assets/images/example.png + router.StaticFS("/public", http.FS(f)) + + router.GET("/", func(c *gin.Context) { + c.HTML(http.StatusOK, "index.tmpl", gin.H{ + "title": "Main website", + }) + }) + + router.GET("/foo", func(c *gin.Context) { + c.HTML(http.StatusOK, "bar.tmpl", gin.H{ + "title": "Foo website", + }) + }) + + router.GET("favicon.ico", func(c *gin.Context) { + file, _ := f.ReadFile("assets/favicon.ico") + c.Data( + http.StatusOK, + "image/x-icon", + file, + ) + }) + + router.Run(":8080") +} +``` + +See a complete example in the `https://github.com/gin-gonic/examples/tree/master/assets-in-binary/example02` directory. + +### Bind form-data request with custom struct + +The follow example using custom struct: + +```go +type StructA struct { + FieldA string `form:"field_a"` +} + +type StructB struct { + NestedStruct StructA + FieldB string `form:"field_b"` +} + +type StructC struct { + NestedStructPointer *StructA + FieldC string `form:"field_c"` +} + +type StructD struct { + NestedAnonyStruct struct { + FieldX string `form:"field_x"` + } + FieldD string `form:"field_d"` +} + +func GetDataB(c *gin.Context) { + var b StructB + c.Bind(&b) + c.JSON(http.StatusOK, gin.H{ + "a": b.NestedStruct, + "b": b.FieldB, + }) +} + +func GetDataC(c *gin.Context) { + var b StructC + c.Bind(&b) + c.JSON(http.StatusOK, gin.H{ + "a": b.NestedStructPointer, + "c": b.FieldC, + }) +} + +func GetDataD(c *gin.Context) { + var b StructD + c.Bind(&b) + c.JSON(http.StatusOK, gin.H{ + "x": b.NestedAnonyStruct, + "d": b.FieldD, + }) +} + +func main() { + r := gin.Default() + r.GET("/getb", GetDataB) + r.GET("/getc", GetDataC) + r.GET("/getd", GetDataD) + + r.Run() +} +``` + +Using the command `curl` command result: + +```sh +$ curl "http://localhost:8080/getb?field_a=hello&field_b=world" +{"a":{"FieldA":"hello"},"b":"world"} +$ curl "http://localhost:8080/getc?field_a=hello&field_c=world" +{"a":{"FieldA":"hello"},"c":"world"} +$ curl "http://localhost:8080/getd?field_x=hello&field_d=world" +{"d":"world","x":{"FieldX":"hello"}} +``` + +### Try to bind body into different structs + +The normal methods for binding request body consumes `c.Request.Body` and they +cannot be called multiple times. + +```go +type formA struct { + Foo string `json:"foo" xml:"foo" binding:"required"` +} + +type formB struct { + Bar string `json:"bar" xml:"bar" binding:"required"` +} + +func SomeHandler(c *gin.Context) { + objA := formA{} + objB := formB{} + // This c.ShouldBind consumes c.Request.Body and it cannot be reused. + if errA := c.ShouldBind(&objA); errA == nil { + c.String(http.StatusOK, `the body should be formA`) + // Always an error is occurred by this because c.Request.Body is EOF now. + } else if errB := c.ShouldBind(&objB); errB == nil { + c.String(http.StatusOK, `the body should be formB`) + } else { + ... + } +} +``` + +For this, you can use `c.ShouldBindBodyWith`. + +```go +func SomeHandler(c *gin.Context) { + objA := formA{} + objB := formB{} + // This reads c.Request.Body and stores the result into the context. + if errA := c.ShouldBindBodyWith(&objA, binding.Form); errA == nil { + c.String(http.StatusOK, `the body should be formA`) + // At this time, it reuses body stored in the context. + } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil { + c.String(http.StatusOK, `the body should be formB JSON`) + // And it can accepts other formats + } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil { + c.String(http.StatusOK, `the body should be formB XML`) + } else { + ... + } +} +``` + +1. `c.ShouldBindBodyWith` stores body into the context before binding. This has +a slight impact to performance, so you should not use this method if you are +enough to call binding at once. +2. This feature is only needed for some formats -- `JSON`, `XML`, `MsgPack`, +`ProtoBuf`. For other formats, `Query`, `Form`, `FormPost`, `FormMultipart`, +can be called by `c.ShouldBind()` multiple times without any damage to +performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)). + +### Bind form-data request with custom struct and custom tag + +```go +const ( + customerTag = "url" + defaultMemory = 32 << 20 +) + +type customerBinding struct {} + +func (customerBinding) Name() string { + return "form" +} + +func (customerBinding) Bind(req *http.Request, obj interface{}) error { + if err := req.ParseForm(); err != nil { + return err + } + if err := req.ParseMultipartForm(defaultMemory); err != nil { + if err != http.ErrNotMultipart { + return err + } + } + if err := binding.MapFormWithTag(obj, req.Form, customerTag); err != nil { + return err + } + return validate(obj) +} + +func validate(obj interface{}) error { + if binding.Validator == nil { + return nil + } + return binding.Validator.ValidateStruct(obj) +} + +// Now we can do this!!! +// FormA is an external type that we can't modify it's tag +type FormA struct { + FieldA string `url:"field_a"` +} + +func ListHandler(s *Service) func(ctx *gin.Context) { + return func(ctx *gin.Context) { + var urlBinding = customerBinding{} + var opt FormA + err := ctx.MustBindWith(&opt, urlBinding) + if err != nil { + ... + } + ... + } +} +``` + +### http2 server push + +http.Pusher is supported only **go1.8+**. See the [golang blog](https://go.dev/blog/h2push) for detail information. + +```go +package main + +import ( + "html/template" + "log" + "net/http" + + "github.com/gin-gonic/gin" +) + +var html = template.Must(template.New("https").Parse(` + + + Https Test + + + +

Welcome, Ginner!

+ + +`)) + +func main() { + r := gin.Default() + r.Static("/assets", "./assets") + r.SetHTMLTemplate(html) + + r.GET("/", func(c *gin.Context) { + if pusher := c.Writer.Pusher(); pusher != nil { + // use pusher.Push() to do server push + if err := pusher.Push("/assets/app.js", nil); err != nil { + log.Printf("Failed to push: %v", err) + } + } + c.HTML(http.StatusOK, "https", gin.H{ + "status": "success", + }) + }) + + // Listen and Server in https://127.0.0.1:8080 + r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key") +} +``` + +### Define format for the log of routes + +The default log of routes is: + +```sh +[GIN-debug] POST /foo --> main.main.func1 (3 handlers) +[GIN-debug] GET /bar --> main.main.func2 (3 handlers) +[GIN-debug] GET /status --> main.main.func3 (3 handlers) +``` + +If you want to log this information in given format (e.g. JSON, key values or something else), then you can define this format with `gin.DebugPrintRouteFunc`. +In the example below, we log all routes with standard log package but you can use another log tools that suits of your needs. + +```go +import ( + "log" + "net/http" + + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { + log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) + } + + r.POST("/foo", func(c *gin.Context) { + c.JSON(http.StatusOK, "foo") + }) + + r.GET("/bar", func(c *gin.Context) { + c.JSON(http.StatusOK, "bar") + }) + + r.GET("/status", func(c *gin.Context) { + c.JSON(http.StatusOK, "ok") + }) + + // Listen and Server in http://0.0.0.0:8080 + r.Run() +} +``` + +### Set and get a cookie + +```go +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +func main() { + + router := gin.Default() + + router.GET("/cookie", func(c *gin.Context) { + + cookie, err := c.Cookie("gin_cookie") + + if err != nil { + cookie = "NotSet" + c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true) + } + + fmt.Printf("Cookie value: %s \n", cookie) + }) + + router.Run() +} +``` + +## Don't trust all proxies + +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 +specify one of these headers. + +Use function `SetTrustedProxies()` on your `gin.Engine` to specify network addresses +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 +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 +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +func main() { + + router := gin.Default() + router.SetTrustedProxies([]string{"192.168.1.2"}) + + router.GET("/", func(c *gin.Context) { + // If the client is 192.168.1.2, use the X-Forwarded-For + // header to deduce the original client IP from the trust- + // worthy parts of that header. + // Otherwise, simply return the direct client IP + fmt.Printf("ClientIP: %s\n", c.ClientIP()) + }) + router.Run() +} +``` + +**Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform` +to skip TrustedProxies check, it has a higher priority than TrustedProxies. +Look at the example below: + +```go +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +func main() { + + router := gin.Default() + // Use predefined header gin.PlatformXXX + router.TrustedPlatform = gin.PlatformGoogleAppEngine + // Or set your own trusted request header for another trusted proxy service + // Don't set it to any suspect request header, it's unsafe + router.TrustedPlatform = "X-CDN-IP" + + router.GET("/", func(c *gin.Context) { + // If you set TrustedPlatform, ClientIP() will resolve the + // corresponding header and return IP directly + fmt.Printf("ClientIP: %s\n", c.ClientIP()) + }) + router.Run() +} +``` + +## Testing + +The `net/http/httptest` package is preferable way for HTTP testing. + +```go +package main + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func setupRouter() *gin.Engine { + r := gin.Default() + r.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) + return r +} + +func main() { + r := setupRouter() + r.Run(":8080") +} +``` + +Test for code example above: + +```go +package main + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPingRoute(t *testing.T) { + router := setupRouter() + + w := httptest.NewRecorder() + req, _ := http.NewRequest(http.MethodGet, "/ping", nil) + router.ServeHTTP(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "pong", w.Body.String()) +} +``` From 41f2669ebcc24dbbef7dcca705a4cd75b7c43f28 Mon Sep 17 00:00:00 2001 From: "Alireza (Pure)" Date: Mon, 2 Jan 2023 07:08:53 +0330 Subject: [PATCH 192/346] console logger HTTP status bug fixed and the corresponding unit test added (#3453) --- response_writer.go | 1 + response_writer_test.go | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/response_writer.go b/response_writer.go index 43e828d712..5cec1f6616 100644 --- a/response_writer.go +++ b/response_writer.go @@ -61,6 +61,7 @@ func (w *responseWriter) WriteHeader(code int) { if code > 0 && w.status != code { if w.Written() { debugPrint("[WARNING] Headers were already written. Wanted to override status code %d with %d", w.status, code) + return } w.status = code } diff --git a/response_writer_test.go b/response_writer_test.go index 57d163c9f1..6fa5ec719c 100644 --- a/response_writer_test.go +++ b/response_writer_test.go @@ -132,3 +132,21 @@ func TestResponseWriterFlush(t *testing.T) { assert.NoError(t, err) assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) } + +func TestResponseWriterStatusCode(t *testing.T) { + testWriter := httptest.NewRecorder() + writer := &responseWriter{} + writer.reset(testWriter) + w := ResponseWriter(writer) + + w.WriteHeader(http.StatusOK) + w.WriteHeaderNow() + + assert.Equal(t, http.StatusOK, w.Status()) + assert.True(t, w.Written()) + + w.WriteHeader(http.StatusUnauthorized) + + // status must be 200 although we tried to change it + assert.Equal(t, http.StatusOK, w.Status()) +} From 7d8fc1563b4e1b4229e61c2fe4c9e31ce13ace7d Mon Sep 17 00:00:00 2001 From: youngxhui Date: Mon, 2 Jan 2023 11:39:26 +0800 Subject: [PATCH 193/346] update context.go Get/Set method use defer (#3429) Using defer to unlock is more in line with go standards --- context.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 737e4d7a37..b1352b9b6e 100644 --- a/context.go +++ b/context.go @@ -248,20 +248,20 @@ func (c *Context) Error(err error) *Error { // It also lazy initializes c.Keys if it was not used previously. func (c *Context) Set(key string, value any) { c.mu.Lock() + defer c.mu.Unlock() if c.Keys == nil { c.Keys = make(map[string]any) } c.Keys[key] = value - c.mu.Unlock() } // Get returns the value for the given key, ie: (value, true). // If the value does not exist it returns (nil, false) func (c *Context) Get(key string) (value any, exists bool) { c.mu.RLock() + defer c.mu.RUnlock() value, exists = c.Keys[key] - c.mu.RUnlock() return } From c9b27249fbb6092bcc7f749811d73ef1d50eee73 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 2 Jan 2023 12:40:48 +0800 Subject: [PATCH 194/346] chore(yaml): upgrade dependency to v3 version (#3456) fixes https://github.com/gin-gonic/gin/issues/3451 fixes https://github.com/gin-gonic/gin/issues/3306 fixes https://github.com/gin-gonic/gin/issues/3362 fixes https://github.com/gin-gonic/gin/issues/2581 --- binding/yaml.go | 2 +- go.mod | 3 +-- go.sum | 2 -- render/render_test.go | 2 +- render/yaml.go | 2 +- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/binding/yaml.go b/binding/yaml.go index b0d36a3589..2535f8c33d 100644 --- a/binding/yaml.go +++ b/binding/yaml.go @@ -9,7 +9,7 @@ import ( "io" "net/http" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) type yamlBinding struct{} diff --git a/go.mod b/go.mod index e969833934..2b7a98cc94 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/ugorji/go/codec v1.2.7 golang.org/x/net v0.4.0 google.golang.org/protobuf v1.28.1 - gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -32,5 +32,4 @@ require ( golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect golang.org/x/sys v0.3.0 // indirect golang.org/x/text v0.5.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6d8df64a0f..a4f0f387ac 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/render/render_test.go b/render/render_test.go index c5c5375f53..ebb7d41472 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -238,7 +238,7 @@ b: err := (YAML{data}).Render(w) assert.NoError(t, err) - assert.Equal(t, "\"\\na : Easy!\\nb:\\n\\tc: 2\\n\\td: [3, 4]\\n\\t\"\n", w.Body.String()) + assert.Equal(t, "|4-\n a : Easy!\n b:\n \tc: 2\n \td: [3, 4]\n \t\n", w.Body.String()) assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type")) } diff --git a/render/yaml.go b/render/yaml.go index 4f0ac01f62..fc927c1f28 100644 --- a/render/yaml.go +++ b/render/yaml.go @@ -7,7 +7,7 @@ package render import ( "net/http" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) // YAML contains the given interface object. From 7626361587bdce4b02335edd1d38627b79027b5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 10:40:53 +0800 Subject: [PATCH 195/346] chore(deps): bump github.com/ugorji/go/codec from 1.2.7 to 1.2.8 (#3458) Bumps [github.com/ugorji/go/codec](https://github.com/ugorji/go) from 1.2.7 to 1.2.8. - [Release notes](https://github.com/ugorji/go/releases) - [Commits](https://github.com/ugorji/go/compare/v1.2.7...v1.2.8) --- updated-dependencies: - dependency-name: github.com/ugorji/go/codec dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 2b7a98cc94..daff61de97 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mattn/go-isatty v0.0.16 github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 - github.com/ugorji/go/codec v1.2.7 + github.com/ugorji/go/codec v1.2.8 golang.org/x/net v0.4.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index a4f0f387ac..c7e0b38a7c 100644 --- a/go.sum +++ b/go.sum @@ -65,9 +65,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/ugorji/go/codec v1.2.8 h1:sgBJS6COt0b/P40VouWKdseidkDgHxYGm0SAglUHfP0= +github.com/ugorji/go/codec v1.2.8/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU= golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= From 79a61b90324586d3c3a5859f8755cae2d1c46f2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 11:39:57 +0800 Subject: [PATCH 196/346] chore(deps): bump github.com/mattn/go-isatty from 0.0.16 to 0.0.17 (#3457) Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.16 to 0.0.17. - [Release notes](https://github.com/mattn/go-isatty/releases) - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.16...v0.0.17) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] 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 daff61de97..8cb21f9c80 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/go-playground/validator/v10 v10.11.1 github.com/goccy/go-json v0.10.0 github.com/json-iterator/go v1.1.12 - github.com/mattn/go-isatty v0.0.16 + github.com/mattn/go-isatty v0.0.17 github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 github.com/ugorji/go/codec v1.2.8 diff --git a/go.sum b/go.sum index c7e0b38a7c..daf51f7d7c 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 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/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= From c58e0d59ca6753da47daa0a64b844bc869030759 Mon Sep 17 00:00:00 2001 From: apriil15 Date: Thu, 5 Jan 2023 10:15:29 +0800 Subject: [PATCH 197/346] docs: update markdown format (#3446) * docs: update markdown format * fix: resolve conflict * docs: update markdown format * docs: update * docs: update * Revert "docs: update" This reverts commit 82716193b753dbcad6fee85973790727b7a31ae5. --- docs/doc.md | 149 +++++++++++++++++++++++++--------------------------- 1 file changed, 73 insertions(+), 76 deletions(-) diff --git a/docs/doc.md b/docs/doc.md index 008a91db78..7cebab566e 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -450,22 +450,22 @@ func main() { ```go func main() { - // Disable Console Color, you don't need console color when writing the logs to file. - gin.DisableConsoleColor() + // Disable Console Color, you don't need console color when writing the logs to file. + gin.DisableConsoleColor() - // Logging to a file. - f, _ := os.Create("gin.log") - gin.DefaultWriter = io.MultiWriter(f) + // Logging to a file. + f, _ := os.Create("gin.log") + gin.DefaultWriter = io.MultiWriter(f) - // Use the following code if you need to write the logs to file and console at the same time. - // gin.DefaultWriter = io.MultiWriter(f, os.Stdout) + // Use the following code if you need to write the logs to file and console at the same time. + // gin.DefaultWriter = io.MultiWriter(f, os.Stdout) - router := gin.Default() - router.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) + router := gin.Default() + router.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) -    router.Run(":8080") +   router.Run(":8080") } ``` @@ -516,18 +516,18 @@ Never colorize logs: ```go func main() { - // Disable log's color - gin.DisableConsoleColor() + // Disable log's color + gin.DisableConsoleColor() - // Creates a gin router with default middleware: - // logger and recovery (crash-free) middleware - router := gin.Default() + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() - router.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) + router.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) - router.Run(":8080") + router.Run(":8080") } ``` @@ -535,18 +535,18 @@ Always colorize logs: ```go func main() { - // Force log's color - gin.ForceConsoleColor() + // Force log's color + gin.ForceConsoleColor() - // Creates a gin router with default middleware: - // logger and recovery (crash-free) middleware - router := gin.Default() + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() - router.GET("/ping", func(c *gin.Context) { - c.String(http.StatusOK, "pong") - }) + router.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) - router.Run(":8080") + router.Run(":8080") } ``` @@ -786,11 +786,11 @@ import ( ) type Person struct { - Name string `form:"name"` - Address string `form:"address"` - Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` - CreateTime time.Time `form:"createTime" time_format:"unixNano"` - UnixTime time.Time `form:"unixTime" time_format:"unix"` + Name string `form:"name"` + Address string `form:"address"` + Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` + CreateTime time.Time `form:"createTime" time_format:"unixNano"` + UnixTime time.Time `form:"unixTime" time_format:"unix"` } func main() { @@ -804,13 +804,13 @@ func startPage(c *gin.Context) { // 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#L88 - if c.ShouldBind(&person) == nil { - log.Println(person.Name) - log.Println(person.Address) - log.Println(person.Birthday) - log.Println(person.CreateTime) - log.Println(person.UnixTime) - } + if c.ShouldBind(&person) == nil { + log.Println(person.Name) + log.Println(person.Address) + log.Println(person.Birthday) + log.Println(person.CreateTime) + log.Println(person.UnixTime) + } c.String(http.StatusOK, "Success") } @@ -1311,34 +1311,34 @@ main.go ```go import ( - "fmt" - "html/template" - "net/http" - "time" + "fmt" + "html/template" + "net/http" + "time" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) func formatAsDate(t time.Time) string { - year, month, day := t.Date() - return fmt.Sprintf("%d/%02d/%02d", year, month, day) + year, month, day := t.Date() + return fmt.Sprintf("%d/%02d/%02d", year, month, day) } func main() { - router := gin.Default() - router.Delims("{[{", "}]}") - router.SetFuncMap(template.FuncMap{ - "formatAsDate": formatAsDate, - }) - router.LoadHTMLFiles("./testdata/template/raw.tmpl") + router := gin.Default() + router.Delims("{[{", "}]}") + router.SetFuncMap(template.FuncMap{ + "formatAsDate": formatAsDate, + }) + router.LoadHTMLFiles("./testdata/template/raw.tmpl") - router.GET("/raw", func(c *gin.Context) { - c.HTML(http.StatusOK, "raw.tmpl", gin.H{ - "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), - }) - }) + router.GET("/raw", func(c *gin.Context) { + c.HTML(http.StatusOK, "raw.tmpl", gin.H{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), + }) + }) - router.Run(":8080") + router.Run(":8080") } ``` @@ -2099,28 +2099,27 @@ func main() { ```go import ( - "fmt" + "fmt" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) func main() { + router := gin.Default() - router := gin.Default() - - router.GET("/cookie", func(c *gin.Context) { + router.GET("/cookie", func(c *gin.Context) { - cookie, err := c.Cookie("gin_cookie") + cookie, err := c.Cookie("gin_cookie") - if err != nil { - cookie = "NotSet" - c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true) - } + if err != nil { + cookie = "NotSet" + c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true) + } - fmt.Printf("Cookie value: %s \n", cookie) - }) + fmt.Printf("Cookie value: %s \n", cookie) + }) - router.Run() + router.Run() } ``` @@ -2149,7 +2148,6 @@ import ( ) func main() { - router := gin.Default() router.SetTrustedProxies([]string{"192.168.1.2"}) @@ -2176,7 +2174,6 @@ import ( ) func main() { - router := gin.Default() // Use predefined header gin.PlatformXXX router.TrustedPlatform = gin.PlatformGoogleAppEngine From 8eb5f832bac1853fc84c508a2b9406134d39492e Mon Sep 17 00:00:00 2001 From: Kristian Svalland <54534849+kristiansvalland@users.noreply.github.com> Date: Sat, 7 Jan 2023 01:57:54 +0100 Subject: [PATCH 198/346] fix(router): tree bug where loop index is not decremented. (#3460) fixes https://github.com/gin-gonic/gin/issues/3459 --- routes_test.go | 19 +++++++++++++++++++ tree.go | 18 +++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/routes_test.go b/routes_test.go index cd8cf14145..ada8e1e457 100644 --- a/routes_test.go +++ b/routes_test.go @@ -670,3 +670,22 @@ func TestRouteContextHoldsFullPath(t *testing.T) { w := PerformRequest(router, http.MethodGet, "/not-found") assert.Equal(t, http.StatusNotFound, w.Code) } + +func TestEngineHandleMethodNotAllowedCornerCase(t *testing.T) { + r := New() + r.HandleMethodNotAllowed = true + + base := r.Group("base") + base.GET("/metrics", handlerTest1) + + v1 := base.Group("v1") + + v1.GET("/:id/devices", handlerTest1) + v1.GET("/user/:id/groups", handlerTest1) + + v1.GET("/orgs/:id", handlerTest1) + v1.DELETE("/orgs/:id", handlerTest1) + + w := PerformRequest(r, "GET", "/base/v1/user/groups") + assert.Equal(t, http.StatusNotFound, w.Code) +} diff --git a/tree.go b/tree.go index 3f34b8ee82..dda8f4f7b0 100644 --- a/tree.go +++ b/tree.go @@ -459,9 +459,9 @@ walk: // Outer loop for walking the tree // If the path at the end of the loop is not equal to '/' and the current node has no child nodes // the current node needs to roll back to last valid skippedNode if path != "/" { - for l := len(*skippedNodes); l > 0; { - skippedNode := (*skippedNodes)[l-1] - *skippedNodes = (*skippedNodes)[:l-1] + for length := len(*skippedNodes); length > 0; length-- { + skippedNode := (*skippedNodes)[length-1] + *skippedNodes = (*skippedNodes)[:length-1] if strings.HasSuffix(skippedNode.path, path) { path = skippedNode.path n = skippedNode.node @@ -576,9 +576,9 @@ walk: // Outer loop for walking the tree // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node // the current node needs to roll back to last valid skippedNode if n.handlers == nil && path != "/" { - for l := len(*skippedNodes); l > 0; { - skippedNode := (*skippedNodes)[l-1] - *skippedNodes = (*skippedNodes)[:l-1] + for length := len(*skippedNodes); length > 0; length-- { + skippedNode := (*skippedNodes)[length-1] + *skippedNodes = (*skippedNodes)[:length-1] if strings.HasSuffix(skippedNode.path, path) { path = skippedNode.path n = skippedNode.node @@ -633,9 +633,9 @@ walk: // Outer loop for walking the tree // roll back to last valid skippedNode if !value.tsr && path != "/" { - for l := len(*skippedNodes); l > 0; { - skippedNode := (*skippedNodes)[l-1] - *skippedNodes = (*skippedNodes)[:l-1] + for length := len(*skippedNodes); length > 0; length-- { + skippedNode := (*skippedNodes)[length-1] + *skippedNodes = (*skippedNodes)[:length-1] if strings.HasSuffix(skippedNode.path, path) { path = skippedNode.path n = skippedNode.node From 47ae6ee386c5b1be402de09c6d69f8d2f0db3c4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 10:11:45 +0800 Subject: [PATCH 199/346] chore(deps): bump golang.org/x/net from 0.4.0 to 0.5.0 (#3466) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.4.0 to 0.5.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.4.0...v0.5.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 8cb21f9c80..0b1d3a6ba4 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 github.com/ugorji/go/codec v1.2.8 - golang.org/x/net v0.4.0 + golang.org/x/net v0.5.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -30,6 +30,6 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect ) diff --git a/go.sum b/go.sum index daf51f7d7c..f86942b5d1 100644 --- a/go.sum +++ b/go.sum @@ -73,20 +73,20 @@ golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUu golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 3010cbd7f4eccdbb610c510274895e083b8c058c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 10:12:12 +0800 Subject: [PATCH 200/346] chore(deps): bump github.com/bytedance/sonic from 1.6.0 to 1.6.1 (#3467) Bumps [github.com/bytedance/sonic](https://github.com/bytedance/sonic) from 1.6.0 to 1.6.1. - [Release notes](https://github.com/bytedance/sonic/releases) - [Commits](https://github.com/bytedance/sonic/compare/v1.6.0...v1.6.1) --- updated-dependencies: - dependency-name: github.com/bytedance/sonic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] 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 0b1d3a6ba4..c498b482ec 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( - github.com/bytedance/sonic v1.6.0 + github.com/bytedance/sonic v1.6.1 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.11.1 github.com/goccy/go-json v0.10.0 diff --git a/go.sum b/go.sum index f86942b5d1..409d393b3a 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.6.0 h1:j90DM/Ss1bmySEQYL2U4jRsUjJ+chASzCCZYxohJR60= -github.com/bytedance/sonic v1.6.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.6.1 h1:HEyWqlvEh95R/rMg5Mh6jDx5Zt35MG24QWzpHMVuan0= +github.com/bytedance/sonic v1.6.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= From 7cb151bb4c4cfc6018a00a125422ff38a041b9f8 Mon Sep 17 00:00:00 2001 From: adrianiacobghiula <2491756+adrianiacobghiula@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:50:07 +0100 Subject: [PATCH 201/346] fix(context): panic on NegotiateFormat - index out of range (#3397) --- context.go | 2 +- context_test.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/context.go b/context.go index b1352b9b6e..0474252709 100644 --- a/context.go +++ b/context.go @@ -1147,7 +1147,7 @@ func (c *Context) NegotiateFormat(offered ...string) string { // According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers, // therefore we can just iterate over the string without casting it into []rune i := 0 - for ; i < len(accepted); i++ { + for ; i < len(accepted) && i < len(offer); i++ { if accepted[i] == '*' || offer[i] == '*' { return offer } diff --git a/context_test.go b/context_test.go index 85e0a6161e..827ee0fafd 100644 --- a/context_test.go +++ b/context_test.go @@ -1311,6 +1311,14 @@ func TestContextNegotiationFormatCustom(t *testing.T) { assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON)) } +func TestContextNegotiationFormat2(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request.Header.Add("Accept", "image/tiff-fx") + + assert.Equal(t, "", c.NegotiateFormat("image/tiff")) +} + func TestContextIsAborted(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) assert.False(t, c.IsAborted()) From 97082f8accd197ec1240b31df3a20993c3747e6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jan 2023 09:58:28 +0800 Subject: [PATCH 202/346] chore(deps): bump github.com/bytedance/sonic from 1.6.1 to 1.7.0 (#3473) Bumps [github.com/bytedance/sonic](https://github.com/bytedance/sonic) from 1.6.1 to 1.7.0. - [Release notes](https://github.com/bytedance/sonic/releases) - [Commits](https://github.com/bytedance/sonic/compare/v1.6.1...v1.7.0) --- updated-dependencies: - dependency-name: github.com/bytedance/sonic dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] 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 c498b482ec..e03001ec7e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( - github.com/bytedance/sonic v1.6.1 + github.com/bytedance/sonic v1.7.0 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.11.1 github.com/goccy/go-json v0.10.0 diff --git a/go.sum b/go.sum index 409d393b3a..4e4e963242 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.6.1 h1:HEyWqlvEh95R/rMg5Mh6jDx5Zt35MG24QWzpHMVuan0= -github.com/bytedance/sonic v1.6.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.7.0 h1:P7DyGrkLbVDzcuqagPsSFnAwwljjhmB3qVF5wzmHOxE= +github.com/bytedance/sonic v1.7.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= From 1660995a04f579b4e0d5683ff45e1af4c2a50346 Mon Sep 17 00:00:00 2001 From: Heliner <32272517+Heliner@users.noreply.github.com> Date: Tue, 17 Jan 2023 14:23:54 +0800 Subject: [PATCH 203/346] Adjust the position of some functions (#3385) Co-authored-by: fredhan --- binding/toml.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/binding/toml.go b/binding/toml.go index a6b8a90abd..a66b93aafe 100644 --- a/binding/toml.go +++ b/binding/toml.go @@ -18,14 +18,6 @@ func (tomlBinding) Name() string { return "toml" } -func decodeToml(r io.Reader, obj any) error { - decoder := toml.NewDecoder(r) - if err := decoder.Decode(obj); err != nil { - return err - } - return decoder.Decode(obj) -} - func (tomlBinding) Bind(req *http.Request, obj any) error { return decodeToml(req.Body, obj) } @@ -33,3 +25,11 @@ func (tomlBinding) Bind(req *http.Request, obj any) error { func (tomlBinding) BindBody(body []byte, obj any) error { return decodeToml(bytes.NewReader(body), obj) } + +func decodeToml(r io.Reader, obj any) error { + decoder := toml.NewDecoder(r) + if err := decoder.Decode(obj); err != nil { + return err + } + return decoder.Decode(obj) +} From 8cd11c82e447f74d63e3da6037cb0463440d8e16 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Tue, 17 Jan 2023 14:26:27 +0800 Subject: [PATCH 204/346] chore(docs): Remove the Brigade project, because the Gin is no longer used in the latest version and the Brigade is an archived CNCF project now (#3378) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index eccf814c2e..336155a990 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,6 @@ Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framewor * [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. * [lura](https://github.com/luraproject/lura): Ultra performant API Gateway with middlewares. * [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. -* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes. * [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. From b2d4185eec36ce5e0cf21be7cb246fb8be9fd6db Mon Sep 17 00:00:00 2001 From: hopehook Date: Fri, 20 Jan 2023 09:51:42 +0800 Subject: [PATCH 205/346] Replace bytes.Buffer with strings.Builder where appropriate (#3347) To build strings more efficiently, use strings.Builder instead. --- debug_test.go | 4 ++-- githubapi_test.go | 4 ++-- logger_test.go | 14 +++++++------- recovery_test.go | 19 +++++++++---------- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/debug_test.go b/debug_test.go index abe8b41cad..ce8b19da7b 100644 --- a/debug_test.go +++ b/debug_test.go @@ -5,7 +5,6 @@ package gin import ( - "bytes" "errors" "fmt" "html/template" @@ -13,6 +12,7 @@ import ( "log" "os" "runtime" + "strings" "sync" "testing" @@ -138,7 +138,7 @@ func captureOutput(t *testing.T, f func()) string { wg := new(sync.WaitGroup) wg.Add(1) go func() { - var buf bytes.Buffer + var buf strings.Builder wg.Done() _, err := io.Copy(&buf, reader) assert.NoError(t, err) diff --git a/githubapi_test.go b/githubapi_test.go index c6350e813e..9276bed508 100644 --- a/githubapi_test.go +++ b/githubapi_test.go @@ -5,12 +5,12 @@ package gin import ( - "bytes" "fmt" "math/rand" "net/http" "net/http/httptest" "os" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -401,7 +401,7 @@ func TestGithubAPI(t *testing.T) { } func exampleFromPath(path string) (string, Params) { - output := new(bytes.Buffer) + output := new(strings.Builder) params := make(Params, 0, 6) start := -1 for i, c := range path { diff --git a/logger_test.go b/logger_test.go index 7bc1137109..5f78708f1a 100644 --- a/logger_test.go +++ b/logger_test.go @@ -5,10 +5,10 @@ package gin import ( - "bytes" "errors" "fmt" "net/http" + "strings" "testing" "time" @@ -20,7 +20,7 @@ func init() { } func TestLogger(t *testing.T) { - buffer := new(bytes.Buffer) + buffer := new(strings.Builder) router := New() router.Use(LoggerWithWriter(buffer)) router.GET("/example", func(c *Context) {}) @@ -84,7 +84,7 @@ func TestLogger(t *testing.T) { } func TestLoggerWithConfig(t *testing.T) { - buffer := new(bytes.Buffer) + buffer := new(strings.Builder) router := New() router.Use(LoggerWithConfig(LoggerConfig{Output: buffer})) router.GET("/example", func(c *Context) {}) @@ -148,7 +148,7 @@ func TestLoggerWithConfig(t *testing.T) { } func TestLoggerWithFormatter(t *testing.T) { - buffer := new(bytes.Buffer) + buffer := new(strings.Builder) d := DefaultWriter DefaultWriter = buffer @@ -182,7 +182,7 @@ func TestLoggerWithFormatter(t *testing.T) { func TestLoggerWithConfigFormatting(t *testing.T) { var gotParam LogFormatterParams var gotKeys map[string]any - buffer := new(bytes.Buffer) + buffer := new(strings.Builder) router := New() router.engine.trustedCIDRs, _ = router.engine.prepareTrustedCIDRs() @@ -382,7 +382,7 @@ func TestErrorLogger(t *testing.T) { } func TestLoggerWithWriterSkippingPaths(t *testing.T) { - buffer := new(bytes.Buffer) + buffer := new(strings.Builder) router := New() router.Use(LoggerWithWriter(buffer, "/skipped")) router.GET("/logged", func(c *Context) {}) @@ -397,7 +397,7 @@ func TestLoggerWithWriterSkippingPaths(t *testing.T) { } func TestLoggerWithConfigSkippingPaths(t *testing.T) { - buffer := new(bytes.Buffer) + buffer := new(strings.Builder) router := New() router.Use(LoggerWithConfig(LoggerConfig{ Output: buffer, diff --git a/recovery_test.go b/recovery_test.go index 347917e7b5..fa8ab894be 100644 --- a/recovery_test.go +++ b/recovery_test.go @@ -5,7 +5,6 @@ package gin import ( - "bytes" "fmt" "net" "net/http" @@ -18,7 +17,7 @@ import ( ) func TestPanicClean(t *testing.T) { - buffer := new(bytes.Buffer) + buffer := new(strings.Builder) router := New() password := "my-super-secret-password" router.Use(RecoveryWithWriter(buffer)) @@ -50,7 +49,7 @@ func TestPanicClean(t *testing.T) { // TestPanicInHandler assert that panic has been recovered. func TestPanicInHandler(t *testing.T) { - buffer := new(bytes.Buffer) + buffer := new(strings.Builder) router := New() router.Use(RecoveryWithWriter(buffer)) router.GET("/recovery", func(_ *Context) { @@ -122,7 +121,7 @@ func TestPanicWithBrokenPipe(t *testing.T) { for errno, expectMsg := range expectMsgs { t.Run(expectMsg, func(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder router := New() router.Use(RecoveryWithWriter(&buf)) @@ -145,8 +144,8 @@ func TestPanicWithBrokenPipe(t *testing.T) { } func TestCustomRecoveryWithWriter(t *testing.T) { - errBuffer := new(bytes.Buffer) - buffer := new(bytes.Buffer) + errBuffer := new(strings.Builder) + buffer := new(strings.Builder) router := New() handleRecovery := func(c *Context, err any) { errBuffer.WriteString(err.(string)) @@ -179,8 +178,8 @@ func TestCustomRecoveryWithWriter(t *testing.T) { } func TestCustomRecovery(t *testing.T) { - errBuffer := new(bytes.Buffer) - buffer := new(bytes.Buffer) + errBuffer := new(strings.Builder) + buffer := new(strings.Builder) router := New() DefaultErrorWriter = buffer handleRecovery := func(c *Context, err any) { @@ -214,8 +213,8 @@ func TestCustomRecovery(t *testing.T) { } func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) { - errBuffer := new(bytes.Buffer) - buffer := new(bytes.Buffer) + errBuffer := new(strings.Builder) + buffer := new(strings.Builder) router := New() DefaultErrorWriter = buffer handleRecovery := func(c *Context, err any) { From ea1787503586f94d7d79323573d35eb3f442a561 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 19:53:10 +0800 Subject: [PATCH 206/346] chore(deps): bump golangci/golangci-lint-action from 3.3.1 to 3.4.0 (#3478) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.1 to 3.4.0. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.3.1...v3.4.0) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action 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> --- .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 7a4e61c617..9bd2698c6d 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Setup golangci-lint - uses: golangci/golangci-lint-action@v3.3.1 + uses: golangci/golangci-lint-action@v3.4.0 with: version: v1.48.0 args: --verbose From c5fd06361b934070e99978c34e1eaef05632bb5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 19:53:45 +0800 Subject: [PATCH 207/346] chore(deps): bump github.com/go-playground/validator/v10 (#3482) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.11.1 to 10.11.2. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.11.1...v10.11.2) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 9 +++++---- go.sum | 40 +++++++++------------------------------- 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index e03001ec7e..ea2c94acd5 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/bytedance/sonic v1.7.0 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.11.1 + github.com/go-playground/validator/v10 v10.11.2 github.com/goccy/go-json v0.10.0 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.17 @@ -20,16 +20,17 @@ require ( require ( github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect github.com/klauspost/cpuid/v2 v2.0.14 // indirect + github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect - golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect + golang.org/x/crypto v0.5.0 // indirect golang.org/x/sys v0.4.0 // indirect golang.org/x/text v0.6.0 // indirect ) diff --git a/go.sum b/go.sum index 4e4e963242..09341d4505 100644 --- a/go.sum +++ b/go.sum @@ -10,14 +10,13 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= +github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -29,12 +28,7 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.14 h1:QRqdp6bb9M9S5yyKeYteXKuoKE4p0tGlra81fKOpWH8= github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= @@ -47,12 +41,9 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -70,36 +61,23 @@ github.com/ugorji/go/codec v1.2.8/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZg golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU= golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From e02ae6ae61fada360379b5bdc7f23e46f21ce5de Mon Sep 17 00:00:00 2001 From: "Alireza (Pure)" Date: Mon, 6 Feb 2023 11:16:42 +0330 Subject: [PATCH 208/346] chore(router): match method added to routergroup for multiple HTTP methods supporting (#3464) --- routergroup.go | 10 ++++++++++ routergroup_test.go | 1 + 2 files changed, 11 insertions(+) diff --git a/routergroup.go b/routergroup.go index dfbdd7b8d3..c833fe8fe3 100644 --- a/routergroup.go +++ b/routergroup.go @@ -42,6 +42,7 @@ type IRoutes interface { PUT(string, ...HandlerFunc) IRoutes OPTIONS(string, ...HandlerFunc) IRoutes HEAD(string, ...HandlerFunc) IRoutes + Match([]string, string, ...HandlerFunc) IRoutes StaticFile(string, string) IRoutes StaticFileFS(string, string, http.FileSystem) IRoutes @@ -151,6 +152,15 @@ func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRou return group.returnObj() } +// Match registers a route that matches the specified methods that you declared. +func (group *RouterGroup) Match(methods []string, relativePath string, handlers ...HandlerFunc) IRoutes { + for _, method := range methods { + group.handle(method, relativePath, handlers) + } + + return group.returnObj() +} + // StaticFile registers a single route in order to serve a single file of the local filesystem. // router.StaticFile("favicon.ico", "./resources/favicon.ico") func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes { diff --git a/routergroup_test.go b/routergroup_test.go index 41f963724e..6848063e29 100644 --- a/routergroup_test.go +++ b/routergroup_test.go @@ -186,6 +186,7 @@ func testRoutesInterface(t *testing.T, r IRoutes) { assert.Equal(t, r, r.PUT("/", handler)) assert.Equal(t, r, r.OPTIONS("/", handler)) assert.Equal(t, r, r.HEAD("/", handler)) + assert.Equal(t, r, r.Match([]string{http.MethodPut, http.MethodPatch}, "/match", handler)) assert.Equal(t, r, r.StaticFile("/file", ".")) assert.Equal(t, r, r.StaticFileFS("/static2", ".", Dir(".", false))) From 153b229fcc6570bac0674d02ab1a629804f29072 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 16:37:36 +0800 Subject: [PATCH 209/346] chore(deps): bump github.com/ugorji/go/codec from 1.2.8 to 1.2.9 (#3491) Bumps [github.com/ugorji/go/codec](https://github.com/ugorji/go) from 1.2.8 to 1.2.9. - [Release notes](https://github.com/ugorji/go/releases) - [Commits](https://github.com/ugorji/go/compare/v1.2.8...v1.2.9) --- updated-dependencies: - dependency-name: github.com/ugorji/go/codec dependency-type: direct:production update-type: version-update:semver-patch ... 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 ea2c94acd5..4ec5325779 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 - github.com/ugorji/go/codec v1.2.8 + github.com/ugorji/go/codec v1.2.9 golang.org/x/net v0.5.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 09341d4505..b2d2f73688 100644 --- a/go.sum +++ b/go.sum @@ -56,8 +56,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.8 h1:sgBJS6COt0b/P40VouWKdseidkDgHxYGm0SAglUHfP0= -github.com/ugorji/go/codec v1.2.8/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= +github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU= golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= From 0c96a20209ca035964be126a745c167196fb6db3 Mon Sep 17 00:00:00 2001 From: Vladislav Dmitriyev Date: Sun, 12 Feb 2023 05:01:33 +0300 Subject: [PATCH 210/346] Stop useless panicking in context and render (#2150) Co-authored-by: Bo-Yi Wu --- context.go | 4 +++- context_test.go | 20 +++++++++----------- render/json.go | 7 ++----- render/render_test.go | 4 ++-- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/context.go b/context.go index 0474252709..556f8ac9cc 100644 --- a/context.go +++ b/context.go @@ -924,7 +924,9 @@ func (c *Context) Render(code int, r render.Render) { } if err := r.Render(c.Writer); err != nil { - panic(err) + // Pushing error to c.Errors + _ = c.Error(err) + c.Abort() } } diff --git a/context_test.go b/context_test.go index 827ee0fafd..1ab6b33949 100644 --- a/context_test.go +++ b/context_test.go @@ -32,6 +32,8 @@ import ( var _ context.Context = (*Context)(nil) +var errTestRender = errors.New("TestRender") + // Unit tests TODO // func (c *Context) File(filepath string) { // func (c *Context) Negotiate(code int, config Negotiate) { @@ -643,25 +645,21 @@ func TestContextBodyAllowedForStatus(t *testing.T) { assert.True(t, true, bodyAllowedForStatus(http.StatusInternalServerError)) } -type TestPanicRender struct{} +type TestRender struct{} -func (*TestPanicRender) Render(http.ResponseWriter) error { - return errors.New("TestPanicRender") +func (*TestRender) Render(http.ResponseWriter) error { + return errTestRender } -func (*TestPanicRender) WriteContentType(http.ResponseWriter) {} +func (*TestRender) WriteContentType(http.ResponseWriter) {} -func TestContextRenderPanicIfErr(t *testing.T) { - defer func() { - r := recover() - assert.Equal(t, "TestPanicRender", fmt.Sprint(r)) - }() +func TestContextRenderIfErr(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Render(http.StatusOK, &TestPanicRender{}) + c.Render(http.StatusOK, &TestRender{}) - assert.Fail(t, "Panic not detected") + assert.Equal(t, errorMsgs{&Error{Err: errTestRender, Type: 1}}, c.Errors) } // Tests that the response is serialized as JSON diff --git a/render/json.go b/render/json.go index af678e80d1..fc8dea453f 100644 --- a/render/json.go +++ b/render/json.go @@ -53,11 +53,8 @@ var ( ) // Render (JSON) writes data with custom ContentType. -func (r JSON) Render(w http.ResponseWriter) (err error) { - if err = WriteJSON(w, r.Data); err != nil { - panic(err) - } - return +func (r JSON) Render(w http.ResponseWriter) error { + return WriteJSON(w, r.Data) } // WriteContentType (JSON) writes JSON ContentType. diff --git a/render/render_test.go b/render/render_test.go index ebb7d41472..192552513f 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -40,12 +40,12 @@ func TestRenderJSON(t *testing.T) { assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } -func TestRenderJSONPanics(t *testing.T) { +func TestRenderJSONError(t *testing.T) { w := httptest.NewRecorder() data := make(chan int) // json: unsupported type: chan int - assert.Panics(t, func() { assert.NoError(t, (JSON{data}).Render(w)) }) + assert.Error(t, (JSON{data}).Render(w)) } func TestRenderIndentedJSON(t *testing.T) { From bd82c9e351be91e9e8267e5ce011627dd6c55d51 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Sun, 12 Feb 2023 13:01:05 +0800 Subject: [PATCH 211/346] =?UTF-8?q?chore(go):=20Add=C2=A0support=20go=201.?= =?UTF-8?q?20=20(#3484)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(go): Add support go 1.20 * Surround the go version parameters with single quotes * chore(deps): bump github.com/bytedance/sonic from v1.7.0 to v1.7.1 --- .github/workflows/gin.yml | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 9bd2698c6d..fac97d4787 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -31,7 +31,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: [1.16, 1.17, 1.18, 1.19] + go: ['1.16', '1.17', '1.18', '1.19', '1.20'] test-tags: ['', '-tags nomsgpack', '-tags "sonic avx"', '-tags go_json'] include: - os: ubuntu-latest diff --git a/go.mod b/go.mod index 4ec5325779..51353f5448 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( - github.com/bytedance/sonic v1.7.0 + github.com/bytedance/sonic v1.7.1 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.11.2 github.com/goccy/go-json v0.10.0 diff --git a/go.sum b/go.sum index b2d2f73688..01f9495294 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.7.0 h1:P7DyGrkLbVDzcuqagPsSFnAwwljjhmB3qVF5wzmHOxE= -github.com/bytedance/sonic v1.7.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.7.1 h1:UYWEKUHQDye89c2U6zvrvuxWdGCI/wCrZITFQmKGtGc= +github.com/bytedance/sonic v1.7.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= From c1d06e3d08692f9eddde381a5e277b41fff5a297 Mon Sep 17 00:00:00 2001 From: David Desmarais-Michaud Date: Sun, 12 Feb 2023 00:01:43 -0500 Subject: [PATCH 212/346] add supprt for go1.20 http.rwUnwrapper to gin.responseWriter (#3489) --- response_writer.go | 4 ++++ response_writer_test.go | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/response_writer.go b/response_writer.go index 5cec1f6616..753a0b09ab 100644 --- a/response_writer.go +++ b/response_writer.go @@ -51,6 +51,10 @@ type responseWriter struct { var _ ResponseWriter = (*responseWriter)(nil) +func (w *responseWriter) Unwrap() http.ResponseWriter { + return w.ResponseWriter +} + func (w *responseWriter) reset(writer http.ResponseWriter) { w.ResponseWriter = writer w.size = noWritten diff --git a/response_writer_test.go b/response_writer_test.go index 6fa5ec719c..9fd5e87cce 100644 --- a/response_writer_test.go +++ b/response_writer_test.go @@ -30,6 +30,12 @@ func init() { SetMode(TestMode) } +func TestResponseWriterUnwrap(t *testing.T) { + testWriter := httptest.NewRecorder() + writer := &responseWriter{ResponseWriter: testWriter} + assert.Same(t, testWriter, writer.Unwrap()) +} + func TestResponseWriterReset(t *testing.T) { testWriter := httptest.NewRecorder() writer := &responseWriter{} From d07db174acf44bfaf191ca2f6d7beafa2ff946da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Feb 2023 10:59:36 +0800 Subject: [PATCH 213/346] chore(deps): bump golang.org/x/net from 0.5.0 to 0.6.0 (#3498) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.6.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.5.0...v0.6.0) --- updated-dependencies: - dependency-name: golang.org/x/net 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 | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 51353f5448..2ef45cfee6 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 github.com/ugorji/go/codec v1.2.9 - golang.org/x/net v0.5.0 + golang.org/x/net v0.6.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -31,6 +31,6 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect golang.org/x/crypto v0.5.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect ) diff --git a/go.sum b/go.sum index 01f9495294..b47804fcc0 100644 --- a/go.sum +++ b/go.sum @@ -63,13 +63,13 @@ golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SX golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= From 81ac7d55a09e34013225db0aeac6e70c1ae68928 Mon Sep 17 00:00:00 2001 From: t0rchwo0d Date: Fri, 17 Feb 2023 11:00:19 +0900 Subject: [PATCH 214/346] Add escape logic for header (#3500) --- gin.go | 4 ++++ routes_test.go | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/gin.go b/gin.go index 35159d03fa..32dae2497f 100644 --- a/gin.go +++ b/gin.go @@ -9,6 +9,7 @@ import ( "html/template" "net" "net/http" + "net/url" "os" "path" "strings" @@ -668,6 +669,9 @@ func redirectTrailingSlash(c *Context) { req := c.Request p := req.URL.Path if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." { + prefix = url.QueryEscape(prefix) + prefix = strings.ReplaceAll(prefix, "%2F", "/") + p = prefix + "/" + req.URL.Path } req.URL.Path = p + "/" diff --git a/routes_test.go b/routes_test.go index ada8e1e457..5310caec25 100644 --- a/routes_test.go +++ b/routes_test.go @@ -185,6 +185,18 @@ func TestRouteRedirectTrailingSlash(t *testing.T) { w = PerformRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"}) assert.Equal(t, 200, w.Code) + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../bug#?"}) + assert.Equal(t, "../../../bug%2523%253F/path", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "https://gin-gonic.com/#"}) + assert.Equal(t, "https%3A/gin-gonic.com/%23/https%253A/gin-gonic.com/%2523/path", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "#bug"}) + assert.Equal(t, "%23bug/%2523bug/path", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + router.RedirectTrailingSlash = false w = PerformRequest(router, http.MethodGet, "/path/") From fc1c43298de675e5252d0b44f97dc5e204bd4e1e Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Sat, 18 Feb 2023 01:43:39 -0500 Subject: [PATCH 215/346] fix(security): vulnerability GO-2023-1571 (#3505) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2ef45cfee6..f7e28d8cae 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 github.com/ugorji/go/codec v1.2.9 - golang.org/x/net v0.6.0 + golang.org/x/net v0.7.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index b47804fcc0..814f4eb377 100644 --- a/go.sum +++ b/go.sum @@ -63,8 +63,8 @@ golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SX golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 4cee78f5382d5245c3652e6c15fee715eec505c3 Mon Sep 17 00:00:00 2001 From: t0rchwo0d Date: Sun, 19 Feb 2023 22:25:48 +0900 Subject: [PATCH 216/346] Fix #3500 Add escape logic for header (#3503) --- gin.go | 9 ++++++--- routes_test.go | 46 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/gin.go b/gin.go index 32dae2497f..f95e5dda54 100644 --- a/gin.go +++ b/gin.go @@ -9,9 +9,9 @@ import ( "html/template" "net" "net/http" - "net/url" "os" "path" + "regexp" "strings" "sync" @@ -41,6 +41,9 @@ var defaultTrustedCIDRs = []*net.IPNet{ }, } +var regSafePrefix = regexp.MustCompile("[^a-zA-Z0-9/-]+") +var regRemoveRepeatedChar = regexp.MustCompile("/{2,}") + // HandlerFunc defines the handler used by gin middleware as return value. type HandlerFunc func(*Context) @@ -669,8 +672,8 @@ func redirectTrailingSlash(c *Context) { req := c.Request p := req.URL.Path if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." { - prefix = url.QueryEscape(prefix) - prefix = strings.ReplaceAll(prefix, "%2F", "/") + prefix = regSafePrefix.ReplaceAllString(prefix, "") + prefix = regRemoveRepeatedChar.ReplaceAllString(prefix, "/") p = prefix + "/" + req.URL.Path } diff --git a/routes_test.go b/routes_test.go index 5310caec25..633c0aba14 100644 --- a/routes_test.go +++ b/routes_test.go @@ -185,16 +185,52 @@ func TestRouteRedirectTrailingSlash(t *testing.T) { w = PerformRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"}) assert.Equal(t, 200, w.Code) - w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../bug#?"}) - assert.Equal(t, "../../../bug%2523%253F/path", w.Header().Get("Location")) + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../api#?"}) + assert.Equal(t, "/api/path", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../api"}) + assert.Equal(t, "/api/path", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "../../api"}) + assert.Equal(t, "/api/path2/", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/../../api"}) + assert.Equal(t, "/api/path2/", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "api/../../"}) + assert.Equal(t, "//path", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "api/../../../"}) + assert.Equal(t, "/path", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "../../gin-gonic.com"}) + assert.Equal(t, "/gin-goniccom/path2/", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/../../gin-gonic.com"}) + assert.Equal(t, "/gin-goniccom/path2/", w.Header().Get("Location")) assert.Equal(t, 301, w.Code) w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "https://gin-gonic.com/#"}) - assert.Equal(t, "https%3A/gin-gonic.com/%23/https%253A/gin-gonic.com/%2523/path", w.Header().Get("Location")) + assert.Equal(t, "https/gin-goniccom/https/gin-goniccom/path", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "#api"}) + assert.Equal(t, "api/api/path", w.Header().Get("Location")) + assert.Equal(t, 301, w.Code) + + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "/nor-mal/#?a=1"}) + assert.Equal(t, "/nor-mal/a1/path", w.Header().Get("Location")) assert.Equal(t, 301, w.Code) - w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "#bug"}) - assert.Equal(t, "%23bug/%2523bug/path", w.Header().Get("Location")) + w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "/nor-mal/%2e%2e/"}) + assert.Equal(t, "/nor-mal/2e2e/path", w.Header().Get("Location")) assert.Equal(t, 301, w.Code) router.RedirectTrailingSlash = false From ea03e10384502e1baf6f560a2b0ea32c342ede5b Mon Sep 17 00:00:00 2001 From: thinkerou Date: Tue, 21 Feb 2023 17:20:32 +0800 Subject: [PATCH 217/346] docs(readme): release v1.9.0 version (#3474) --- CHANGELOG.md | 79 ++++++++++++++++++++++++++++++++++++++++++---------- go.mod | 6 ++-- go.sum | 10 +++---- version.go | 2 +- 4 files changed, 73 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ab5617924..cf24ec2867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,49 @@ # Gin ChangeLog +## Gin v1.9.0 + +### BREAK CHANGES + +* Stop useless panicking in context and render [#2150](https://github.com/gin-gonic/gin/pull/2150) + +### BUG FIXES + +* fix(router): tree bug where loop index is not decremented. [#3460](https://github.com/gin-gonic/gin/pull/3460) +* fix(context): panic on NegotiateFormat - index out of range [#3397](https://github.com/gin-gonic/gin/pull/3397) +* Add escape logic for header [#3500](https://github.com/gin-gonic/gin/pull/3500) and [#3503](https://github.com/gin-gonic/gin/pull/3503) + +### SECURITY + +* Fix the GO-2022-0969 and GO-2022-0288 vulnerabilities [#3333](https://github.com/gin-gonic/gin/pull/3333) +* fix(security): vulnerability GO-2023-1571 [#3505](https://github.com/gin-gonic/gin/pull/3505) + +### ENHANCEMENTS + +* feat: add sonic json support [#3184](https://github.com/gin-gonic/gin/pull/3184) +* chore(file): Creates a directory named path [#3316](https://github.com/gin-gonic/gin/pull/3316) +* fix: modify interface check way [#3327](https://github.com/gin-gonic/gin/pull/3327) +* remove deprecated of package io/ioutil [#3395](https://github.com/gin-gonic/gin/pull/3395) +* refactor: avoid calling strings.ToLower twice [#3343](https://github.com/gin-gonic/gin/pull/3433) +* console logger HTTP status code bug fixed [#3453](https://github.com/gin-gonic/gin/pull/3453) +* chore(yaml): upgrade dependency to v3 version [#3456](https://github.com/gin-gonic/gin/pull/3456) +* chore(router): match method added to routergroup for multiple HTTP methods supporting [#3464](https://github.com/gin-gonic/gin/pull/3464) +* chore(http): add support for go1.20 http.rwUnwrapper to gin.responseWriter [#3489](https://github.com/gin-gonic/gin/pull/3489) + +### DOCS + +* docs: update markdown format [#3260](https://github.com/gin-gonic/gin/pull/3260) +* docs(readme): Add the TOML rendering example [#3400](https://github.com/gin-gonic/gin/pull/3400) +* docs(readme): move more example to docs/doc.md [#3449](https://github.com/gin-gonic/gin/pull/3449) +* docs: update markdown format [#3446](https://github.com/gin-gonic/gin/pull/3446) + ## Gin v1.8.2 -### Bugs +### BUG FIXES * fix(route): redirectSlash bug ([#3227]((https://github.com/gin-gonic/gin/pull/3227))) * fix(engine): missing route params for CreateTestContext ([#2778]((https://github.com/gin-gonic/gin/pull/2778))) ([#2803]((https://github.com/gin-gonic/gin/pull/2803))) -### Security +### SECURITY * Fix the GO-2022-1144 vulnerability ([#3432]((https://github.com/gin-gonic/gin/pull/3432))) @@ -19,12 +55,12 @@ ## Gin v1.8.0 -## Break Changes +### BREAK CHANGES * TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967). Please replace `RemoteIP() (net.IP, bool)` with `RemoteIP() net.IP` * gin.Context with fallback value from gin.Context.Request.Context() [#2751](https://github.com/gin-gonic/gin/pull/2751) -### BUGFIXES +### BUG FIXES * Fixed SetOutput() panics on go 1.17 [#2861](https://github.com/gin-gonic/gin/pull/2861) * Fix: wrong when wildcard follows named param [#2983](https://github.com/gin-gonic/gin/pull/2983) @@ -61,7 +97,7 @@ ## Gin v1.7.7 -### BUGFIXES +### BUG FIXES * 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). @@ -79,37 +115,37 @@ ## Gin v1.7.6 -### BUGFIXES +### BUG FIXES * bump new release to fix v1.7.5 release error by using v1.7.4 codes. ## Gin v1.7.4 -### BUGFIXES +### BUG FIXES * bump new release to fix checksum mismatch ## Gin v1.7.3 -### BUGFIXES +### BUG FIXES * fix level 1 router match [#2767](https://github.com/gin-gonic/gin/issues/2767), [#2796](https://github.com/gin-gonic/gin/issues/2796) ## Gin v1.7.2 -### BUGFIXES +### BUG FIXES * Fix conflict between param and exact path [#2706](https://github.com/gin-gonic/gin/issues/2706). Close issue [#2682](https://github.com/gin-gonic/gin/issues/2682) [#2696](https://github.com/gin-gonic/gin/issues/2696). ## Gin v1.7.1 -### BUGFIXES +### BUG FIXES * fix: data race with trustedCIDRs from [#2674](https://github.com/gin-gonic/gin/issues/2674)([#2675](https://github.com/gin-gonic/gin/pull/2675)) ## Gin v1.7.0 -### BUGFIXES +### BUG FIXES * fix compile error from [#2572](https://github.com/gin-gonic/gin/pull/2572) ([#2600](https://github.com/gin-gonic/gin/pull/2600)) * fix: print headers without Authorization header on broken pipe ([#2528](https://github.com/gin-gonic/gin/pull/2528)) @@ -148,33 +184,44 @@ ## Gin v1.6.2 -### BUGFIXES +### BUG FIXES + * fix missing initial sync.RWMutex [#2305](https://github.com/gin-gonic/gin/pull/2305) + ### ENHANCEMENTS + * Add set samesite in cookie. [#2306](https://github.com/gin-gonic/gin/pull/2306) ## Gin v1.6.1 -### BUGFIXES +### BUG FIXES + * Revert "fix accept incoming network connections" [#2294](https://github.com/gin-gonic/gin/pull/2294) ## Gin v1.6.0 ### BREAKING + * chore(performance): Improve performance for adding RemoveExtraSlash flag [#2159](https://github.com/gin-gonic/gin/pull/2159) * drop support govendor [#2148](https://github.com/gin-gonic/gin/pull/2148) * Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615) + ### FEATURES + * add yaml negotiation [#2220](https://github.com/gin-gonic/gin/pull/2220) * FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112) -### BUGFIXES + +### BUG FIXES + * Unix Socket Handling [#2280](https://github.com/gin-gonic/gin/pull/2280) * Use json marshall in context json to fix breaking new line issue. Fixes #2209 [#2228](https://github.com/gin-gonic/gin/pull/2228) * fix accept incoming network connections [#2216](https://github.com/gin-gonic/gin/pull/2216) * Fixed a bug in the calculation of the maximum number of parameters [#2166](https://github.com/gin-gonic/gin/pull/2166) * [FIX] allow empty headers on DataFromReader [#2121](https://github.com/gin-gonic/gin/pull/2121) * Add mutex for protect Context.Keys map [#1391](https://github.com/gin-gonic/gin/pull/1391) + ### ENHANCEMENTS + * Add mitigation for log injection [#2277](https://github.com/gin-gonic/gin/pull/2277) * tree: range over nodes values [#2229](https://github.com/gin-gonic/gin/pull/2229) * tree: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222) @@ -189,7 +236,9 @@ * upgrade go-validator to v10 [#2149](https://github.com/gin-gonic/gin/pull/2149) * Refactor redirect request in gin.go [#1970](https://github.com/gin-gonic/gin/pull/1970) * Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852) + ### DOCS + * docs(path): improve comments [#2223](https://github.com/gin-gonic/gin/pull/2223) * Renew README to fit the modification of SetCookie method [#2217](https://github.com/gin-gonic/gin/pull/2217) * Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202) @@ -202,7 +251,9 @@ * Add project to README [#2165](https://github.com/gin-gonic/gin/pull/2165) * docs(benchmarks): for gin v1.5 [#2153](https://github.com/gin-gonic/gin/pull/2153) * Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122) + ### MISC + * ci support go1.14 [#2262](https://github.com/gin-gonic/gin/pull/2262) * chore: upgrade depend version [#2231](https://github.com/gin-gonic/gin/pull/2231) * Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147) diff --git a/go.mod b/go.mod index f7e28d8cae..db36337e32 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( - github.com/bytedance/sonic v1.7.1 + github.com/bytedance/sonic v1.8.0 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.11.2 github.com/goccy/go-json v0.10.0 @@ -22,14 +22,14 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/klauspost/cpuid/v2 v2.0.14 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/crypto v0.5.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect diff --git a/go.sum b/go.sum index 814f4eb377..8bdb934a1f 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.7.1 h1:UYWEKUHQDye89c2U6zvrvuxWdGCI/wCrZITFQmKGtGc= -github.com/bytedance/sonic v1.7.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= +github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= @@ -25,9 +25,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.14 h1:QRqdp6bb9M9S5yyKeYteXKuoKE4p0tGlra81fKOpWH8= -github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -58,9 +57,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU= -golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= diff --git a/version.go b/version.go index 37e27f27a5..390da4f3e4 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.8.2" +const Version = "v1.9.0" From 0b5df9fc3992bde6e13fd71b795ff4f8b27d4f65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:42:49 +0800 Subject: [PATCH 218/346] chore(deps): bump github.com/bytedance/sonic from 1.7.1 to 1.8.1 (#3508) Bumps [github.com/bytedance/sonic](https://github.com/bytedance/sonic) from 1.7.1 to 1.8.1. - [Release notes](https://github.com/bytedance/sonic/releases) - [Commits](https://github.com/bytedance/sonic/compare/v1.7.1...v1.8.1) --- updated-dependencies: - dependency-name: github.com/bytedance/sonic 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 db36337e32..3ec4780096 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( - github.com/bytedance/sonic v1.8.0 + github.com/bytedance/sonic v1.8.1 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.11.2 github.com/goccy/go-json v0.10.0 diff --git a/go.sum b/go.sum index 8bdb934a1f..d6a91933f1 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= -github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.8.1 h1:NqAHCaGaTzro0xMmnTCLUyRlbEP6r8MCA1cJUrH3Pu4= +github.com/bytedance/sonic v1.8.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= From 943e93cba04808294d0748b74bcdc8322b8ebaa7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:43:24 +0800 Subject: [PATCH 219/346] chore(deps): bump github.com/ugorji/go/codec from 1.2.9 to 1.2.10 (#3509) Bumps [github.com/ugorji/go/codec](https://github.com/ugorji/go) from 1.2.9 to 1.2.10. - [Release notes](https://github.com/ugorji/go/releases) - [Commits](https://github.com/ugorji/go/compare/v1.2.9...v1.2.10) --- updated-dependencies: - dependency-name: github.com/ugorji/go/codec dependency-type: direct:production update-type: version-update:semver-patch ... 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 3ec4780096..da978740e8 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 - github.com/ugorji/go/codec v1.2.9 + github.com/ugorji/go/codec v1.2.10 golang.org/x/net v0.7.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index d6a91933f1..cab49ab059 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= -github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.10 h1:eimT6Lsr+2lzmSZxPhLFoOWFmQqwk0fllJJ5hEbTXtQ= +github.com/ugorji/go/codec v1.2.10/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= From 1e1f0b1e76b89b48542171e2c5ee829a69c2b91f Mon Sep 17 00:00:00 2001 From: thinkerou Date: Wed, 1 Mar 2023 10:03:48 +0800 Subject: [PATCH 220/346] chore: support min go version 1.18 (#3511) * chore: min go version 1.18 * fix build tag error * remove build tag * fix word * remove any.go * replace interface{} instead of any --- .github/workflows/gin.yml | 6 +- .github/workflows/goreleaser.yml | 2 +- README.md | 2 +- any.go | 10 --- binding/any.go | 10 --- binding/binding.go | 1 - binding/binding_msgpack_test.go | 1 - binding/binding_nomsgpack.go | 1 - binding/json.go | 2 +- binding/msgpack.go | 1 - binding/msgpack_test.go | 1 - context.go | 6 +- context_1.17_test.go | 94 -------------------- context_1.16_test.go => context_1.18_test.go | 20 +++-- context_1.19_test.go | 1 - context_appengine.go | 1 - context_test.go | 41 ++++++++- debug.go | 4 +- debug_test.go | 4 +- deprecated.go | 2 +- docs/doc.md | 8 +- internal/json/go_json.go | 1 - internal/json/json.go | 3 - internal/json/jsoniter.go | 1 - internal/json/sonic.go | 4 - render/any.go | 10 --- render/msgpack.go | 1 - render/render_msgpack_test.go | 1 - testdata/protoexample/any.go | 10 --- utils.go | 2 +- 30 files changed, 72 insertions(+), 179 deletions(-) delete mode 100644 any.go delete mode 100644 binding/any.go delete mode 100644 context_1.17_test.go rename context_1.16_test.go => context_1.18_test.go (66%) delete mode 100644 render/any.go delete mode 100644 testdata/protoexample/any.go diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index fac97d4787..5c1504a93e 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -18,7 +18,7 @@ jobs: - name: Setup go uses: actions/setup-go@v3 with: - go-version: '^1.16' + go-version: '^1.18' - name: Checkout repository uses: actions/checkout@v3 - name: Setup golangci-lint @@ -31,7 +31,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: ['1.16', '1.17', '1.18', '1.19', '1.20'] + go: ['1.18', '1.19', '1.20'] test-tags: ['', '-tags nomsgpack', '-tags "sonic avx"', '-tags go_json'] include: - os: ubuntu-latest @@ -73,7 +73,7 @@ jobs: flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }} - name: Format - if: matrix.go-version == '1.19.x' + if: matrix.go-version == '1.20.x' run: diff -u <(echo -n) <(gofmt -d .) notification-gitter: needs: test diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 3af3a455e1..baf02af562 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -21,7 +21,7 @@ jobs: name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.20 - name: Run GoReleaser uses: goreleaser/goreleaser-action@v4 diff --git a/README.md b/README.md index 336155a990..cba54ab8fa 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Gin is a web framework written in [Go](https://go.dev/). It features a martini-l ### Prerequisites -- **[Go](https://go.dev/)**: ~~any one of the **three latest major** [releases](https://go.dev/doc/devel/release)~~ (now version **1.16+** is required). +- **[Go](https://go.dev/)**: any one of the **three latest major** [releases](https://go.dev/doc/devel/release) (we test it with these). ### Getting Gin diff --git a/any.go b/any.go deleted file mode 100644 index 42b1ea46f7..0000000000 --- a/any.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2022 Gin Core Team. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package gin - -type any = interface{} diff --git a/binding/any.go b/binding/any.go deleted file mode 100644 index d8251a7c20..0000000000 --- a/binding/any.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2022 Gin Core Team. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package binding - -type any = interface{} diff --git a/binding/binding.go b/binding/binding.go index a58924ed3e..40948529a3 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !nomsgpack -// +build !nomsgpack package binding diff --git a/binding/binding_msgpack_test.go b/binding/binding_msgpack_test.go index 04d9407971..a6cd6aa83d 100644 --- a/binding/binding_msgpack_test.go +++ b/binding/binding_msgpack_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !nomsgpack -// +build !nomsgpack package binding diff --git a/binding/binding_nomsgpack.go b/binding/binding_nomsgpack.go index 7f6a904ab0..93ad8ba304 100644 --- a/binding/binding_nomsgpack.go +++ b/binding/binding_nomsgpack.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build nomsgpack -// +build nomsgpack package binding diff --git a/binding/json.go b/binding/json.go index 36eb27a344..e21c2ee346 100644 --- a/binding/json.go +++ b/binding/json.go @@ -15,7 +15,7 @@ import ( // EnableDecoderUseNumber is used to call the UseNumber method on the JSON // Decoder instance. UseNumber causes the Decoder to unmarshal a number into an -// interface{} as a Number instead of as a float64. +// any as a Number instead of as a float64. var EnableDecoderUseNumber = false // EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method diff --git a/binding/msgpack.go b/binding/msgpack.go index d1f035e44a..22de9b5515 100644 --- a/binding/msgpack.go +++ b/binding/msgpack.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !nomsgpack -// +build !nomsgpack package binding diff --git a/binding/msgpack_test.go b/binding/msgpack_test.go index 11561c843d..df386a6d56 100644 --- a/binding/msgpack_test.go +++ b/binding/msgpack_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !nomsgpack -// +build !nomsgpack package binding diff --git a/context.go b/context.go index 556f8ac9cc..5716318e1f 100644 --- a/context.go +++ b/context.go @@ -652,7 +652,7 @@ func (c *Context) BindYAML(obj any) error { } // BindTOML is a shortcut for c.MustBindWith(obj, binding.TOML). -func (c *Context) BindTOML(obj interface{}) error { +func (c *Context) BindTOML(obj any) error { return c.MustBindWith(obj, binding.TOML) } @@ -717,7 +717,7 @@ func (c *Context) ShouldBindYAML(obj any) error { } // ShouldBindTOML is a shortcut for c.ShouldBindWith(obj, binding.TOML). -func (c *Context) ShouldBindTOML(obj interface{}) error { +func (c *Context) ShouldBindTOML(obj any) error { return c.ShouldBindWith(obj, binding.TOML) } @@ -995,7 +995,7 @@ func (c *Context) YAML(code int, obj any) { } // TOML serializes the given struct as TOML into the response body. -func (c *Context) TOML(code int, obj interface{}) { +func (c *Context) TOML(code int, obj any) { c.Render(code, render.TOML{Data: obj}) } diff --git a/context_1.17_test.go b/context_1.17_test.go deleted file mode 100644 index 0f8527fe09..0000000000 --- a/context_1.17_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2021 Gin Core Team. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build go1.17 -// +build go1.17 - -package gin - -import ( - "bytes" - "mime/multipart" - "net/http" - "net/http/httptest" - "runtime" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -type interceptedWriter struct { - ResponseWriter - b *bytes.Buffer -} - -func (i interceptedWriter) WriteHeader(code int) { - i.Header().Del("X-Test") - i.ResponseWriter.WriteHeader(code) -} - -func TestContextFormFileFailed17(t *testing.T) { - if !isGo117OrGo118() { - return - } - buf := new(bytes.Buffer) - mw := multipart.NewWriter(buf) - defer func(mw *multipart.Writer) { - err := mw.Close() - if err != nil { - assert.Error(t, err) - } - }(mw) - c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) - c.Request.Header.Set("Content-Type", mw.FormDataContentType()) - c.engine.MaxMultipartMemory = 8 << 20 - assert.Panics(t, func() { - f, err := c.FormFile("file") - assert.Error(t, err) - assert.Nil(t, f) - }) -} - -func TestInterceptedHeader(t *testing.T) { - w := httptest.NewRecorder() - c, r := CreateTestContext(w) - - r.Use(func(c *Context) { - i := interceptedWriter{ - ResponseWriter: c.Writer, - b: bytes.NewBuffer(nil), - } - c.Writer = i - c.Next() - c.Header("X-Test", "overridden") - c.Writer = i.ResponseWriter - }) - r.GET("/", func(c *Context) { - c.Header("X-Test", "original") - c.Header("X-Test-2", "present") - c.String(http.StatusOK, "hello world") - }) - c.Request = httptest.NewRequest("GET", "/", nil) - r.HandleContext(c) - // Result() has headers frozen when WriteHeaderNow() has been called - // Compared to this time, this is when the response headers will be flushed - // As response is flushed on c.String, the Header cannot be set by the first - // middleware. Assert this - assert.Equal(t, "", w.Result().Header.Get("X-Test")) - assert.Equal(t, "present", w.Result().Header.Get("X-Test-2")) -} - -func isGo117OrGo118() bool { - version := strings.Split(runtime.Version()[2:], ".") - if len(version) >= 2 { - x := version[0] - y := version[1] - if x == "1" && (y == "17" || y == "18") { - return true - } - } - return false -} diff --git a/context_1.16_test.go b/context_1.18_test.go similarity index 66% rename from context_1.16_test.go rename to context_1.18_test.go index 267605072e..6118beaa49 100644 --- a/context_1.16_test.go +++ b/context_1.18_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. -//go:build !go1.17 -// +build !go1.17 +//go:build !go1.19 package gin @@ -17,15 +16,22 @@ import ( "github.com/stretchr/testify/assert" ) -func TestContextFormFileFailed16(t *testing.T) { +func TestContextFormFileFailed18(t *testing.T) { buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) - mw.Close() + defer func(mw *multipart.Writer) { + err := mw.Close() + if err != nil { + assert.Error(t, err) + } + }(mw) c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) c.engine.MaxMultipartMemory = 8 << 20 - f, err := c.FormFile("file") - assert.Error(t, err) - assert.Nil(t, f) + assert.Panics(t, func() { + f, err := c.FormFile("file") + assert.Error(t, err) + assert.Nil(t, f) + }) } diff --git a/context_1.19_test.go b/context_1.19_test.go index 4b34ea2472..dd75325b18 100644 --- a/context_1.19_test.go +++ b/context_1.19_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.19 -// +build go1.19 package gin diff --git a/context_appengine.go b/context_appengine.go index 931313f613..96b339c488 100644 --- a/context_appengine.go +++ b/context_appengine.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build appengine -// +build appengine package gin diff --git a/context_test.go b/context_test.go index 1ab6b33949..1dec902c69 100644 --- a/context_test.go +++ b/context_test.go @@ -37,7 +37,7 @@ var errTestRender = errors.New("TestRender") // Unit tests TODO // func (c *Context) File(filepath string) { // func (c *Context) Negotiate(code int, config Negotiate) { -// BAD case: func (c *Context) Render(code int, render render.Render, obj ...interface{}) { +// BAD case: func (c *Context) Render(code int, render render.Render, obj ...any) { // test that information is not leaked when reusing Contexts (using the Pool) func createMultipartRequest() *http.Request { @@ -2374,3 +2374,42 @@ func TestCreateTestContextWithRouteParams(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "hello gin", w.Body.String()) } + +type interceptedWriter struct { + ResponseWriter + b *bytes.Buffer +} + +func (i interceptedWriter) WriteHeader(code int) { + i.Header().Del("X-Test") + i.ResponseWriter.WriteHeader(code) +} + +func TestInterceptedHeader(t *testing.T) { + w := httptest.NewRecorder() + c, r := CreateTestContext(w) + + r.Use(func(c *Context) { + i := interceptedWriter{ + ResponseWriter: c.Writer, + b: bytes.NewBuffer(nil), + } + c.Writer = i + c.Next() + c.Header("X-Test", "overridden") + c.Writer = i.ResponseWriter + }) + r.GET("/", func(c *Context) { + c.Header("X-Test", "original") + c.Header("X-Test-2", "present") + c.String(http.StatusOK, "hello world") + }) + c.Request = httptest.NewRequest("GET", "/", nil) + r.HandleContext(c) + // Result() has headers frozen when WriteHeaderNow() has been called + // Compared to this time, this is when the response headers will be flushed + // As response is flushed on c.String, the Header cannot be set by the first + // middleware. Assert this + assert.Equal(t, "", w.Result().Header.Get("X-Test")) + assert.Equal(t, "present", w.Result().Header.Get("X-Test-2")) +} diff --git a/debug.go b/debug.go index cbcedbc98f..1fc0cafe10 100644 --- a/debug.go +++ b/debug.go @@ -12,7 +12,7 @@ import ( "strings" ) -const ginSupportMinGoVer = 16 +const ginSupportMinGoVer = 18 // IsDebugging returns true if the framework is running in debug mode. // Use SetMode(gin.ReleaseMode) to disable debug mode. @@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) { func debugPrintWARNINGDefault() { if v, e := getMinVer(runtime.Version()); e == nil && v < ginSupportMinGoVer { - debugPrint(`[WARNING] Now Gin requires Go 1.16+. + debugPrint(`[WARNING] Now Gin requires Go 1.18+. `) } diff --git a/debug_test.go b/debug_test.go index ce8b19da7b..2d5e9a5600 100644 --- a/debug_test.go +++ b/debug_test.go @@ -21,7 +21,7 @@ import ( // TODO // func debugRoute(httpMethod, absolutePath string, handlers HandlersChain) { -// func debugPrint(format string, values ...interface{}) { +// func debugPrint(format string, values ...any) { func TestIsDebugging(t *testing.T) { SetMode(DebugMode) @@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { }) m, e := getMinVer(runtime.Version()) if e == nil && m < ginSupportMinGoVer { - assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.16+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) + assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.18+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } else { assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } diff --git a/deprecated.go b/deprecated.go index fdad855477..9521308f1d 100644 --- a/deprecated.go +++ b/deprecated.go @@ -13,7 +13,7 @@ import ( // BindWith binds the passed struct pointer using the specified binding engine. // See the binding package. func (c *Context) BindWith(obj any, b binding.Binding) error { - log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to + log.Println(`BindWith(\"any, binding.Binding\") error is going to be deprecated, please check issue #662 and either use MustBindWith() if you want HTTP 400 to be automatically returned if any error occur, or use ShouldBindWith() if you need to manage the error.`) diff --git a/docs/doc.md b/docs/doc.md index 7cebab566e..e48c2ba183 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -425,7 +425,7 @@ func main() { r.Use(gin.Logger()) // Recovery middleware recovers from any panics and writes a 500 if there was one. - r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) { + r.Use(gin.CustomRecovery(func(c *gin.Context, recovered any) { if err, ok := recovered.(string); ok { c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) } @@ -996,7 +996,7 @@ curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost: func main() { r := gin.Default() - // gin.H is a shortcut for map[string]interface{} + // gin.H is a shortcut for map[string]any r.GET("/someJSON", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) }) @@ -1961,7 +1961,7 @@ func (customerBinding) Name() string { return "form" } -func (customerBinding) Bind(req *http.Request, obj interface{}) error { +func (customerBinding) Bind(req *http.Request, obj any) error { if err := req.ParseForm(); err != nil { return err } @@ -1976,7 +1976,7 @@ func (customerBinding) Bind(req *http.Request, obj interface{}) error { return validate(obj) } -func validate(obj interface{}) error { +func validate(obj any) error { if binding.Validator == nil { return nil } diff --git a/internal/json/go_json.go b/internal/json/go_json.go index 23f717265e..47c3559831 100644 --- a/internal/json/go_json.go +++ b/internal/json/go_json.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go_json -// +build go_json package json diff --git a/internal/json/json.go b/internal/json/json.go index c5f3efc88d..c7ee83eb3b 100644 --- a/internal/json/json.go +++ b/internal/json/json.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build !jsoniter && !go_json && !(sonic && avx && (linux || windows || darwin) && amd64) -// +build !jsoniter -// +build !go_json -// +build !sonic !avx !linux,!windows,!darwin !amd64 package json diff --git a/internal/json/jsoniter.go b/internal/json/jsoniter.go index 853b1a901e..45ed16ba9f 100644 --- a/internal/json/jsoniter.go +++ b/internal/json/jsoniter.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build jsoniter -// +build jsoniter package json diff --git a/internal/json/sonic.go b/internal/json/sonic.go index 5a9ca4b2d0..529e16d072 100644 --- a/internal/json/sonic.go +++ b/internal/json/sonic.go @@ -3,10 +3,6 @@ // license that can be found in the LICENSE file. //go:build sonic && avx && (linux || windows || darwin) && amd64 -// +build sonic -// +build avx -// +build linux windows darwin -// +build amd64 package json diff --git a/render/any.go b/render/any.go deleted file mode 100644 index b19ad45d9f..0000000000 --- a/render/any.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2021 Gin Core Team. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package render - -type any = interface{} diff --git a/render/msgpack.go b/render/msgpack.go index e0f30f7a97..d1d8e84b03 100644 --- a/render/msgpack.go +++ b/render/msgpack.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !nomsgpack -// +build !nomsgpack package render diff --git a/render/render_msgpack_test.go b/render/render_msgpack_test.go index 642123614e..db4b71e5ee 100644 --- a/render/render_msgpack_test.go +++ b/render/render_msgpack_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !nomsgpack -// +build !nomsgpack package render diff --git a/testdata/protoexample/any.go b/testdata/protoexample/any.go deleted file mode 100644 index 2203f33a32..0000000000 --- a/testdata/protoexample/any.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2021 Gin Core Team. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package protoexample - -type any = interface{} diff --git a/utils.go b/utils.go index 4021a2ab40..47106a7aea 100644 --- a/utils.go +++ b/utils.go @@ -50,7 +50,7 @@ func WrapH(h http.Handler) HandlerFunc { } } -// H is a shortcut for map[string]interface{} +// H is a shortcut for map[string]any type H map[string]any // MarshalXML allows type H to be used with xml.Marshal. From d1b2408027e3dc61215e0591ef8735107848cbb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Mar 2023 10:04:56 +0800 Subject: [PATCH 221/346] chore(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#3515) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index da978740e8..d52e73cb69 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.17 github.com/pelletier/go-toml/v2 v2.0.6 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/ugorji/go/codec v1.2.10 golang.org/x/net v0.7.0 google.golang.org/protobuf v1.28.1 diff --git a/go.sum b/go.sum index cab49ab059..bb8225b317 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.10 h1:eimT6Lsr+2lzmSZxPhLFoOWFmQqwk0fllJJ5hEbTXtQ= From 457fabd7e14f36ca1b5f302f7247efeb4690e49c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Mar 2023 10:05:28 +0800 Subject: [PATCH 222/346] chore(deps): bump github.com/bytedance/sonic from 1.8.1 to 1.8.2 (#3516) Bumps [github.com/bytedance/sonic](https://github.com/bytedance/sonic) from 1.8.1 to 1.8.2. - [Release notes](https://github.com/bytedance/sonic/releases) - [Commits](https://github.com/bytedance/sonic/compare/v1.8.1...v1.8.2) --- updated-dependencies: - dependency-name: github.com/bytedance/sonic dependency-type: direct:production update-type: version-update:semver-patch ... 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 d52e73cb69..484e388b34 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.18 require ( - github.com/bytedance/sonic v1.8.1 + github.com/bytedance/sonic v1.8.2 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.11.2 github.com/goccy/go-json v0.10.0 diff --git a/go.sum b/go.sum index bb8225b317..6193e008bd 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.1 h1:NqAHCaGaTzro0xMmnTCLUyRlbEP6r8MCA1cJUrH3Pu4= -github.com/bytedance/sonic v1.8.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.8.2 h1:Eq1oE3xWIBE3tj2ZtJFK1rDAx7+uA4bRytozVhXMHKY= +github.com/bytedance/sonic v1.8.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= From de1c4ec54616e30ecf2a6645e596ad5aacaff2c9 Mon Sep 17 00:00:00 2001 From: lgbgbl <65756378+lgbgbl@users.noreply.github.com> Date: Wed, 1 Mar 2023 13:57:15 +0800 Subject: [PATCH 223/346] refactor: use bytes.ReplaceAll directly (#3455) --- recovery.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recovery.go b/recovery.go index 2955c03a0e..037be51a6b 100644 --- a/recovery.go +++ b/recovery.go @@ -164,7 +164,7 @@ func function(pc uintptr) []byte { if period := bytes.Index(name, dot); period >= 0 { name = name[period+1:] } - name = bytes.Replace(name, centerDot, dot, -1) + name = bytes.ReplaceAll(name, centerDot, dot) return name } From a889c58de78711cb9b53de6cfcc9272c8518c729 Mon Sep 17 00:00:00 2001 From: hopehook Date: Thu, 2 Mar 2023 08:12:20 +0800 Subject: [PATCH 224/346] Convert strings and slices using the officially recommended way (#3344) * Feat: Convert strings and slices using the officially recommended way. Go official is expected to provide unsafe.{SliceData, Slice, StringData, String} series methods in version 1.20 for conversion of strings and slices. * chore: add reference documentation link to comment of code * chore: update Copyright * chore: remove build tag "+build !go1.20" --- go.mod | 2 +- .../{bytesconv.go => bytesconv_1.19.go} | 2 ++ internal/bytesconv/bytesconv_1.20.go | 23 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) rename internal/bytesconv/{bytesconv.go => bytesconv_1.19.go} (96%) create mode 100644 internal/bytesconv/bytesconv_1.20.go diff --git a/go.mod b/go.mod index 484e388b34..0358006d3f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gin-gonic/gin -go 1.18 +go 1.20 require ( github.com/bytedance/sonic v1.8.2 diff --git a/internal/bytesconv/bytesconv.go b/internal/bytesconv/bytesconv_1.19.go similarity index 96% rename from internal/bytesconv/bytesconv.go rename to internal/bytesconv/bytesconv_1.19.go index 86e4c4d44c..669c9c914e 100644 --- a/internal/bytesconv/bytesconv.go +++ b/internal/bytesconv/bytesconv_1.19.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. +//go:build !go1.20 + package bytesconv import ( diff --git a/internal/bytesconv/bytesconv_1.20.go b/internal/bytesconv/bytesconv_1.20.go new file mode 100644 index 0000000000..5b6040a6b3 --- /dev/null +++ b/internal/bytesconv/bytesconv_1.20.go @@ -0,0 +1,23 @@ +// Copyright 2023 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build go1.20 + +package bytesconv + +import ( + "unsafe" +) + +// StringToBytes converts string to byte slice without a memory allocation. +// For more details, see https://github.com/golang/go/issues/53003#issuecomment-1140276077. +func StringToBytes(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) +} + +// BytesToString converts byte slice to string without a memory allocation. +// For more details, see https://github.com/golang/go/issues/53003#issuecomment-1140276077. +func BytesToString(b []byte) string { + return unsafe.String(unsafe.SliceData(b), len(b)) +} From fe989b6a6f8091b2708b39a60b1dd2a2bd3b2812 Mon Sep 17 00:00:00 2001 From: Dylan Maassen van den Brink Date: Wed, 26 Apr 2023 05:18:22 +0200 Subject: [PATCH 225/346] docs: changed documentation link for trusted proxies (#3575) --- gin.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gin.go b/gin.go index f95e5dda54..ed8b6dad75 100644 --- a/gin.go +++ b/gin.go @@ -515,7 +515,7 @@ func (engine *Engine) RunUnix(file string) (err error) { if engine.isUnsafeTrustedProxies() { debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + - "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") } listener, err := net.Listen("unix", file) @@ -538,7 +538,7 @@ func (engine *Engine) RunFd(fd int) (err error) { if engine.isUnsafeTrustedProxies() { debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + - "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") } f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd)) @@ -559,7 +559,7 @@ func (engine *Engine) RunListener(listener net.Listener) (err error) { if engine.isUnsafeTrustedProxies() { debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + - "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") } err = http.Serve(listener, engine.Handler()) From 757a638b7bbdd998375432fb22f693e82d7a7840 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Wed, 26 Apr 2023 14:13:56 +0800 Subject: [PATCH 226/346] chore: improve linting, testing, and GitHub Actions setup (#3583) - Update golangci-lint version from `v1.48.0` to `v1.52.2` - Remove Gitter notifications from GitHub Actions workflow - Add gosec linter settings and include specific rules - Exclude revive linter for test files - Remove Gitter badge from README.md - Delete codecov.yml file - Change function parameter name in fs.go - Remove unused parameter in defaultHandleRecovery function Signed-off-by: appleboy --- .github/workflows/gin.yml | 16 +--------------- .golangci.yml | 19 +++++++++++++++++++ README.md | 3 +-- codecov.yml | 5 ----- fs.go | 2 +- recovery.go | 2 +- 6 files changed, 23 insertions(+), 24 deletions(-) delete mode 100644 codecov.yml diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 5c1504a93e..b758c7fa2c 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -24,7 +24,7 @@ jobs: - name: Setup golangci-lint uses: golangci/golangci-lint-action@v3.4.0 with: - version: v1.48.0 + version: v1.52.2 args: --verbose test: needs: lint @@ -75,17 +75,3 @@ jobs: - name: Format if: matrix.go-version == '1.20.x' run: diff -u <(echo -n) <(gofmt -d .) - notification-gitter: - needs: test - runs-on: ubuntu-latest - steps: - - name: Notification failure message - if: failure() - run: | - PR_OR_COMPARE="$(if [ "${{ github.event.pull_request }}" != "" ]; then echo "${{ github.event.pull_request.html_url }}"; else echo "${{ github.event.compare }}"; fi)" - curl -d message="GitHub Actions [$GITHUB_REPOSITORY]($PR_OR_COMPARE) ($GITHUB_REF) [normal]($GITHUB_API_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID) ($GITHUB_RUN_NUMBER)" -d level=error https://webhooks.gitter.im/e/7f95bf605c4d356372f4 - - name: Notification success message - if: success() - run: | - PR_OR_COMPARE="$(if [ "${{ github.event.pull_request }}" != "" ]; then echo "${{ github.event.pull_request.html_url }}"; else echo "${{ github.event.compare }}"; fi)" - curl -d message="GitHub Actions [$GITHUB_REPOSITORY]($PR_OR_COMPARE) ($GITHUB_REF) [normal]($GITHUB_API_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID) ($GITHUB_RUN_NUMBER)" https://webhooks.gitter.im/e/7f95bf605c4d356372f4 diff --git a/.golangci.yml b/.golangci.yml index c5e1de388a..91dae02c39 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,6 +19,22 @@ linters: - nolintlint - revive - wastedassign + +linters-settings: + gosec: + # To select a subset of rules to run. + # Available rules: https://github.com/securego/gosec#available-rules + # Default: [] - means include all rules + includes: + - G102 + - G106 + - G108 + - G109 + - G111 + - G112 + - G201 + - G203 + issues: exclude-rules: - linters: @@ -37,3 +53,6 @@ issues: - path: _test\.go linters: - gosec # security is not make sense in tests + - linters: + - revive + path: _test\.go diff --git a/README.md b/README.md index cba54ab8fa..e007bf2fbb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ [![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin) [![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin) [![GoDoc](https://pkg.go.dev/badge/github.com/gin-gonic/gin?status.svg)](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc) -[![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge) [![Open Source Helpers](https://www.codetriage.com/gin-gonic/gin/badges/users.svg)](https://www.codetriage.com/gin-gonic/gin) [![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases) @@ -176,4 +175,4 @@ Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framewor Gin is the work of hundreds of contributors. We appreciate your help! -Please see [CONTRIBUTING](CONTRIBUTING.md) for details on submitting patches and the contribution workflow. \ No newline at end of file +Please see [CONTRIBUTING](CONTRIBUTING.md) for details on submitting patches and the contribution workflow. diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index c9c9a522da..0000000000 --- a/codecov.yml +++ /dev/null @@ -1,5 +0,0 @@ -coverage: - notify: - gitter: - default: - url: https://webhooks.gitter.im/e/d90dcdeeab2f1e357165 diff --git a/fs.go b/fs.go index 64274735e2..f17d7434d9 100644 --- a/fs.go +++ b/fs.go @@ -39,7 +39,7 @@ func (fs onlyFilesFS) Open(name string) (http.File, error) { } // Readdir overrides the http.File default implementation. -func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) { +func (f neuteredReaddirFile) Readdir(_ int) ([]os.FileInfo, error) { // this disables directory listing return nil, nil } diff --git a/recovery.go b/recovery.go index 037be51a6b..515f9d2adc 100644 --- a/recovery.go +++ b/recovery.go @@ -103,7 +103,7 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc { } } -func defaultHandleRecovery(c *Context, err any) { +func defaultHandleRecovery(c *Context, _ any) { c.AbortWithStatus(http.StatusInternalServerError) } From eac2daac64811197970b5d2f6406e4ae6c31cb5e Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 27 Apr 2023 10:16:59 +0800 Subject: [PATCH 227/346] chore: update dependencies for various packages and libraries (#3585) - Update bytedance/sonic to v1.8.8 - Update go-playground/validator/v10 to v10.12.0 - Update goccy/go-json to v0.10.2 - Update mattn/go-isatty to v0.0.18 - Update pelletier/go-toml/v2 to v2.0.7 - Update ugorji/go/codec to v1.2.11 - Update golang.org/x/net to v0.9.0 - Update google.golang.org/protobuf to v1.30.0 - Update klauspost/cpuid/v2 to v2.2.4 - Update leodido/go-urn to v1.2.3 - Update modern-go/concurrent to v0.0.0-20180306012644-bacd9c7ef1dd - Update golang.org/x/arch to v0.3.0 - Update golang.org/x/crypto to v0.8.0 - Update golang.org/x/sys to v0.7.0 - Update golang.org/x/text to v0.9.0 Signed-off-by: appleboy --- go.mod | 31 +++++++++++++------------- go.sum | 68 ++++++++++++++++++++++++++++------------------------------ 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/go.mod b/go.mod index 0358006d3f..5fa6200490 100644 --- a/go.mod +++ b/go.mod @@ -3,17 +3,17 @@ module github.com/gin-gonic/gin go 1.20 require ( - github.com/bytedance/sonic v1.8.2 + github.com/bytedance/sonic v1.8.8 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.11.2 - github.com/goccy/go-json v0.10.0 + github.com/go-playground/validator/v10 v10.12.0 + github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 - github.com/mattn/go-isatty v0.0.17 - github.com/pelletier/go-toml/v2 v2.0.6 + github.com/mattn/go-isatty v0.0.18 + github.com/pelletier/go-toml/v2 v2.0.7 github.com/stretchr/testify v1.8.2 - github.com/ugorji/go/codec v1.2.10 - golang.org/x/net v0.7.0 - google.golang.org/protobuf v1.28.1 + github.com/ugorji/go/codec v1.2.11 + golang.org/x/net v0.9.0 + google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -22,15 +22,14 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/leodido/go-urn v1.2.1 // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.3 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/crypto v0.5.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.8.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect ) diff --git a/go.sum b/go.sum index 6193e008bd..90704f4e46 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,9 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.2 h1:Eq1oE3xWIBE3tj2ZtJFK1rDAx7+uA4bRytozVhXMHKY= -github.com/bytedance/sonic v1.8.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q= +github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,39 +14,36 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= -github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= -github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= -github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= +github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/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= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -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/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= +github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= +github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= 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/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -56,26 +52,28 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.10 h1:eimT6Lsr+2lzmSZxPhLFoOWFmQqwk0fllJJ5hEbTXtQ= -github.com/ugorji/go/codec v1.2.10/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 6a0556ed5a67d1d12ae3e7ea2c0121b6c3b894e2 Mon Sep 17 00:00:00 2001 From: ccpro <92025731+CCpro10@users.noreply.github.com> Date: Wed, 10 May 2023 17:19:26 +0800 Subject: [PATCH 228/346] improve render code coverage (#3525) --- render/render_test.go | 46 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/render/render_test.go b/render/render_test.go index 192552513f..86dc362de2 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -15,6 +15,7 @@ import ( "strings" "testing" + "github.com/gin-gonic/gin/internal/json" testdata "github.com/gin-gonic/gin/testdata/protoexample" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/proto" @@ -136,6 +137,51 @@ func TestRenderJsonpJSON(t *testing.T) { assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type")) } +type errorWriter struct { + bufString string + *httptest.ResponseRecorder +} + +var _ http.ResponseWriter = (*errorWriter)(nil) + +func (w *errorWriter) Write(buf []byte) (int, error) { + if string(buf) == w.bufString { + return 0, errors.New(`write "` + w.bufString + `" error`) + } + return w.ResponseRecorder.Write(buf) +} + +func TestRenderJsonpJSONError(t *testing.T) { + ew := &errorWriter{ + ResponseRecorder: httptest.NewRecorder(), + } + + jsonpJSON := JsonpJSON{ + Callback: "foo", + Data: map[string]string{ + "foo": "bar", + }, + } + + cb := template.JSEscapeString(jsonpJSON.Callback) + ew.bufString = cb + err := jsonpJSON.Render(ew) // error was returned while writing callback + assert.Equal(t, `write "`+cb+`" error`, err.Error()) + + ew.bufString = `(` + err = jsonpJSON.Render(ew) + assert.Equal(t, `write "`+`(`+`" error`, err.Error()) + + data, _ := json.Marshal(jsonpJSON.Data) // error was returned while writing data + ew.bufString = string(data) + err = jsonpJSON.Render(ew) + assert.Equal(t, `write "`+string(data)+`" error`, err.Error()) + + ew.bufString = `);` + err = jsonpJSON.Render(ew) + assert.Equal(t, `write "`+`);`+`" error`, err.Error()) +} + func TestRenderJsonpJSONError2(t *testing.T) { w := httptest.NewRecorder() data := map[string]any{ From 1ab268989db62a6dd86264cb20e14160e25a6a6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 16:45:22 +0800 Subject: [PATCH 229/346] chore(deps): bump golang.org/x/net from 0.9.0 to 0.10.0 (#3599) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.9.0 to 0.10.0. - [Commits](https://github.com/golang/net/compare/v0.9.0...v0.10.0) --- updated-dependencies: - dependency-name: golang.org/x/net 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 | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 5fa6200490..3f3aa0eb77 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.7 github.com/stretchr/testify v1.8.2 github.com/ugorji/go/codec v1.2.11 - golang.org/x/net v0.9.0 + golang.org/x/net v0.10.0 google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -30,6 +30,6 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.8.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect ) diff --git a/go.sum b/go.sum index 90704f4e46..84502906f9 100644 --- a/go.sum +++ b/go.sum @@ -59,12 +59,12 @@ golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= From 6bdc725c8dfdc203530f1c64c7ea1aaaf4aeaa40 Mon Sep 17 00:00:00 2001 From: Hiroki Nakano <33213547+hirokinakano@users.noreply.github.com> Date: Fri, 26 May 2023 12:45:46 +0900 Subject: [PATCH 230/346] Fix typos in ISSUE_TEMPLATE.md (#3616) --- .github/ISSUE_TEMPLATE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6f8288d58a..864787cac0 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -30,7 +30,7 @@ func main() { ``` -$ curl http://localhost:8201/hello/world +$ curl http://localhost:9000/hello/world Hello world ``` @@ -38,7 +38,7 @@ Hello world ``` -$ curl -i http://localhost:8201/hello/world +$ curl -i http://localhost:9000/hello/world ``` From 20cd6bcfc41148ae4acb01290496f818a61306aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 11:47:59 +0800 Subject: [PATCH 231/346] chore(deps): bump github.com/go-playground/validator/v10 (#3610) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.12.0 to 10.14.0. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.12.0...v10.14.0) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 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 | 5 +++-- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 3f3aa0eb77..7ec3e4fda4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/bytedance/sonic v1.8.8 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.12.0 + github.com/go-playground/validator/v10 v10.14.0 github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.18 @@ -20,10 +20,11 @@ require ( require ( github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.3 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index 84502906f9..36d6b84e44 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583j github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -14,8 +16,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -27,8 +29,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= -github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= From 9f5ecd4be440f2789db917aa93c1b8afa2276917 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 11:50:31 +0800 Subject: [PATCH 232/346] chore(deps): bump actions/setup-go from 3 to 4 (#3543) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gin.yml | 4 ++-- .github/workflows/goreleaser.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index b758c7fa2c..df6e194e9c 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Setup go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.18' - name: Checkout repository @@ -46,7 +46,7 @@ jobs: GOPROXY: https://proxy.golang.org steps: - name: Set up Go ${{ matrix.go }} - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: ${{ matrix.go }} diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index baf02af562..5b205bab7a 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -19,7 +19,7 @@ jobs: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: 1.20 - From 2d4bbec941551479b1fdf1e54ece03e6e82a7e72 Mon Sep 17 00:00:00 2001 From: Motoyasu Saburi Date: Mon, 29 May 2023 10:57:53 +0900 Subject: [PATCH 233/346] fix lack of escaping of filename in Content-Disposition (#3556) * fix lack of escaping of filename in Content-Disposition * add test for Content-Disposition filename escaping process * fix filename escape bypass problem fix backslashes before backquotes were not properly escaped problem. --- context.go | 8 +++++++- context_test.go | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/context.go b/context.go index 5716318e1f..cb360879c6 100644 --- a/context.go +++ b/context.go @@ -1052,11 +1052,17 @@ func (c *Context) FileFromFS(filepath string, fs http.FileSystem) { http.FileServer(fs).ServeHTTP(c.Writer, c.Request) } +var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") + +func escapeQuotes(s string) string { + return quoteEscaper.Replace(s) +} + // FileAttachment writes the specified file into the body stream in an efficient way // On the client side, the file will typically be downloaded with the given filename func (c *Context) FileAttachment(filepath, filename string) { if isASCII(filename) { - c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`) + c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+escapeQuotes(filename)+`"`) } else { c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename)) } diff --git a/context_test.go b/context_test.go index 1dec902c69..180512356d 100644 --- a/context_test.go +++ b/context_test.go @@ -1032,6 +1032,20 @@ func TestContextRenderAttachment(t *testing.T) { assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition")) } +func TestContextRenderAndEscapeAttachment(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + maliciousFilename := "tampering_field.sh\"; \\\"; dummy=.go" + actualEscapedResponseFilename := "tampering_field.sh\\\"; \\\\\\\"; dummy=.go" + + c.Request, _ = http.NewRequest("GET", "/", nil) + c.FileAttachment("./gin.go", maliciousFilename) + + assert.Equal(t, 200, w.Code) + assert.Contains(t, w.Body.String(), "func New() *Engine {") + assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", actualEscapedResponseFilename), w.Header().Get("Content-Disposition")) +} + func TestContextRenderUTF8Attachment(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) From bb1fc2e0fe97c63dab1527baab88d01183853b8f Mon Sep 17 00:00:00 2001 From: Bence Vidosits <38434845+bvidosits@users.noreply.github.com> Date: Mon, 29 May 2023 01:59:35 +0000 Subject: [PATCH 234/346] fix Request.Context() checks (#3512) Co-authored-by: Bence Vidosits --- context.go | 15 +++++++++++---- context_test.go | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/context.go b/context.go index cb360879c6..420ff1678f 100644 --- a/context.go +++ b/context.go @@ -1180,9 +1180,16 @@ func (c *Context) SetAccepted(formats ...string) { /***** GOLANG.ORG/X/NET/CONTEXT *****/ /************************************/ +// hasRequestContext returns whether c.Request has Context and fallback. +func (c *Context) hasRequestContext() bool { + hasFallback := c.engine != nil && c.engine.ContextWithFallback + hasRequestContext := c.Request != nil && c.Request.Context() != nil + return hasFallback && hasRequestContext +} + // Deadline returns that there is no deadline (ok==false) when c.Request has no Context. func (c *Context) Deadline() (deadline time.Time, ok bool) { - if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil { + if !c.hasRequestContext() { return } return c.Request.Context().Deadline() @@ -1190,7 +1197,7 @@ func (c *Context) Deadline() (deadline time.Time, ok bool) { // Done returns nil (chan which will wait forever) when c.Request has no Context. func (c *Context) Done() <-chan struct{} { - if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil { + if !c.hasRequestContext() { return nil } return c.Request.Context().Done() @@ -1198,7 +1205,7 @@ func (c *Context) Done() <-chan struct{} { // Err returns nil when c.Request has no Context. func (c *Context) Err() error { - if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil { + if !c.hasRequestContext() { return nil } return c.Request.Context().Err() @@ -1219,7 +1226,7 @@ func (c *Context) Value(key any) any { return val } } - if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil { + if !c.hasRequestContext() { return nil } return c.Request.Context().Value(key) diff --git a/context_test.go b/context_test.go index 180512356d..70d4758377 100644 --- a/context_test.go +++ b/context_test.go @@ -2176,6 +2176,24 @@ func TestRemoteIPFail(t *testing.T) { assert.False(t, trust) } +func TestHasRequestContext(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + assert.False(t, c.hasRequestContext(), "no request, no fallback") + c.engine.ContextWithFallback = true + assert.False(t, c.hasRequestContext(), "no request, has fallback") + c.Request, _ = http.NewRequest(http.MethodGet, "/", nil) + assert.True(t, c.hasRequestContext(), "has request, has fallback") + c.Request, _ = http.NewRequestWithContext(nil, "", "", nil) //nolint:staticcheck + assert.False(t, c.hasRequestContext(), "has request with nil ctx, has fallback") + c.engine.ContextWithFallback = false + assert.False(t, c.hasRequestContext(), "has request, no fallback") + + c = &Context{} + assert.False(t, c.hasRequestContext(), "no request, no engine") + c.Request, _ = http.NewRequest(http.MethodGet, "/", nil) + assert.False(t, c.hasRequestContext(), "has request, no engine") +} + func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) // enable ContextWithFallback feature flag From 4ea0e648e38a63d6caff14100f5eab5c50912bcd Mon Sep 17 00:00:00 2001 From: Adriano Sela Aviles Date: Wed, 31 May 2023 19:26:20 -0700 Subject: [PATCH 235/346] Ready release gin 1.9.1 (by: thinkerou) (#3630) * upgrade deps version * update change log * update version * update go mod * fix cr --------- Co-authored-by: thinkerou --- CHANGELOG.md | 21 +++++++++++++++++++++ go.mod | 10 +++++----- go.sum | 26 +++++++++++++++++--------- version.go | 2 +- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf24ec2867..7968520553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Gin ChangeLog +## Gin v1.9.1 + +### BUG FIXES + +* fix Request.Context() checks [#3512](https://github.com/gin-gonic/gin/pull/3512) + +### SECURITY + +* fix lack of escaping of filename in Content-Disposition [#3556](https://github.com/gin-gonic/gin/pull/3556) + +### ENHANCEMENTS + +* refactor: use bytes.ReplaceAll directly [#3455](https://github.com/gin-gonic/gin/pull/3455) +* convert strings and slices using the officially recommended way [#3344](https://github.com/gin-gonic/gin/pull/3344) +* improve render code coverage [#3525](https://github.com/gin-gonic/gin/pull/3525) + +### DOCS + +* docs: changed documentation link for trusted proxies [#3575](https://github.com/gin-gonic/gin/pull/3575) +* chore: improve linting, testing, and GitHub Actions setup [#3583](https://github.com/gin-gonic/gin/pull/3583) + ## Gin v1.9.0 ### BREAK CHANGES diff --git a/go.mod b/go.mod index 7ec3e4fda4..e37698ea19 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,14 @@ module github.com/gin-gonic/gin go 1.20 require ( - github.com/bytedance/sonic v1.8.8 + github.com/bytedance/sonic v1.9.1 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.14.0 github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 - github.com/mattn/go-isatty v0.0.18 - github.com/pelletier/go-toml/v2 v2.0.7 - github.com/stretchr/testify v1.8.2 + github.com/mattn/go-isatty v0.0.19 + github.com/pelletier/go-toml/v2 v2.0.8 + github.com/stretchr/testify v1.8.3 github.com/ugorji/go/codec v1.2.11 golang.org/x/net v0.10.0 google.golang.org/protobuf v1.30.0 @@ -30,7 +30,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.8.0 // indirect + golang.org/x/crypto v0.9.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect ) diff --git a/go.sum b/go.sum index 36d6b84e44..0a91a3e6c3 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q= -github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.9.0 h1:iwLYBds8bYtzwOX7kmdYwtS+aY2GgekVoIs2/IxR0tM= +github.com/bytedance/sonic v1.9.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= @@ -26,20 +28,22 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -50,17 +54,21 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/version.go b/version.go index 390da4f3e4..85462e5553 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.9.0" +const Version = "v1.9.1" From d4a64265f21993368c90602c18e778bf04ef36db Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 5 Jun 2023 09:52:39 +0800 Subject: [PATCH 236/346] chore(CI): update release args (#3595) --- .github/workflows/goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 5b205bab7a..07a054834e 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -29,6 +29,6 @@ jobs: # either 'goreleaser' (default) or 'goreleaser-pro' distribution: goreleaser version: latest - args: release --rm-dist + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 02e754be9c4889f7ee56db0660cc611eb82b61d6 Mon Sep 17 00:00:00 2001 From: C <6714828+cpcf@users.noreply.github.com> Date: Fri, 4 Aug 2023 03:58:46 +0100 Subject: [PATCH 237/346] Upgrade golang.org/x/net -> v0.13.0 (#3684) Patches https://security.snyk.io/vuln/SNYK-GOLANG-GOLANGORGXNETHTML-5816820 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index e37698ea19..ded1334a93 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.8 github.com/stretchr/testify v1.8.3 github.com/ugorji/go/codec v1.2.11 - golang.org/x/net v0.10.0 + golang.org/x/net v0.13.0 google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v3 v3.0.1 ) From 62b50cfbc0de877207ff74c160a23dff6394f563 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 10 Aug 2023 17:06:34 +0800 Subject: [PATCH 238/346] chore: update dependencies to latest versions (#3694) - Update the version of `golang.org/x/crypto` from `v0.9.0` to `v0.11.0` - Update the version of `golang.org/x/sys` from `v0.8.0` to `v0.10.0` - Update the version of `golang.org/x/text` from `v0.9.0` to `v0.11.0` Signed-off-by: Bo-Yi Wu --- go.mod | 6 +++--- go.sum | 23 ++++++++--------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index ded1334a93..e129548504 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.9.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect ) diff --git a/go.sum b/go.sum index 0a91a3e6c3..147a110a93 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,4 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.0 h1:iwLYBds8bYtzwOX7kmdYwtS+aY2GgekVoIs2/IxR0tM= -github.com/bytedance/sonic v1.9.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= @@ -28,7 +26,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= @@ -36,7 +33,6 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -61,22 +57,19 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= From d16fdb15fa54ba898bf6f6ed757397282ed9e496 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Aug 2023 09:23:47 +0800 Subject: [PATCH 239/346] chore(deps): bump golang.org/x/net from 0.13.0 to 0.14.0 (#3688) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index e129548504..b133475d63 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.8 github.com/stretchr/testify v1.8.3 github.com/ugorji/go/codec v1.2.11 - golang.org/x/net v0.13.0 + golang.org/x/net v0.14.0 google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -30,7 +30,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.11.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + golang.org/x/crypto v0.12.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect ) diff --git a/go.sum b/go.sum index 147a110a93..a2d587abe4 100644 --- a/go.sum +++ b/go.sum @@ -60,16 +60,16 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= -golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= From bb2d8cf486bde2dc69bf05ea917095260ac13723 Mon Sep 17 00:00:00 2001 From: Leonardo de Araujo <46436462+araujo88@users.noreply.github.com> Date: Sat, 12 Aug 2023 11:21:56 -0300 Subject: [PATCH 240/346] test(render): increased unit tests coverage (#3691) --- render/render_test.go | 13 +++++++++++++ response_writer_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/render/render_test.go b/render/render_test.go index 86dc362de2..c9db635f04 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -578,3 +578,16 @@ func TestRenderReaderNoContentLength(t *testing.T) { assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition")) assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id")) } + +func TestRenderWriteError(t *testing.T) { + data := []interface{}{"value1", "value2"} + prefix := "my-prefix:" + r := SecureJSON{Data: data, Prefix: prefix} + ew := &errorWriter{ + bufString: prefix, + ResponseRecorder: httptest.NewRecorder(), + } + err := r.Render(ew) + assert.NotNil(t, err) + assert.Equal(t, `write "my-prefix:" error`, err.Error()) +} diff --git a/response_writer_test.go b/response_writer_test.go index 9fd5e87cce..964aa3071e 100644 --- a/response_writer_test.go +++ b/response_writer_test.go @@ -156,3 +156,33 @@ func TestResponseWriterStatusCode(t *testing.T) { // status must be 200 although we tried to change it assert.Equal(t, http.StatusOK, w.Status()) } + +// mockPusherResponseWriter is an http.ResponseWriter that implements http.Pusher. +type mockPusherResponseWriter struct { + http.ResponseWriter +} + +func (m *mockPusherResponseWriter) Push(target string, opts *http.PushOptions) error { + return nil +} + +// nonPusherResponseWriter is an http.ResponseWriter that does not implement http.Pusher. +type nonPusherResponseWriter struct { + http.ResponseWriter +} + +func TestPusherWithPusher(t *testing.T) { + rw := &mockPusherResponseWriter{} + w := &responseWriter{ResponseWriter: rw} + + pusher := w.Pusher() + assert.NotNil(t, pusher, "Expected pusher to be non-nil") +} + +func TestPusherWithoutPusher(t *testing.T) { + rw := &nonPusherResponseWriter{} + w := &responseWriter{ResponseWriter: rw} + + pusher := w.Pusher() + assert.Nil(t, pusher, "Expected pusher to be nil") +} From e32b5e3a47c1aa238dc312fcddc45182a5b90032 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 27 Aug 2023 16:58:10 +0800 Subject: [PATCH 241/346] chore(deps): bump golangci/golangci-lint-action from 3.4.0 to 3.7.0 (#3703) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 df6e194e9c..54d76bb430 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Setup golangci-lint - uses: golangci/golangci-lint-action@v3.4.0 + uses: golangci/golangci-lint-action@v3.7.0 with: version: v1.52.2 args: --verbose From dc9cff732e27ce4ac21b25772a83c462a28b8b80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 27 Aug 2023 16:58:36 +0800 Subject: [PATCH 242/346] chore(deps): bump github.com/go-playground/validator/v10 from 10.14.0 to 10.15.1 (#3702) 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 b133475d63..5c2ec05dfb 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/bytedance/sonic v1.9.1 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.14.0 + github.com/go-playground/validator/v10 v10.15.1 github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.19 diff --git a/go.sum b/go.sum index a2d587abe4..b992f59421 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM= +github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= From c2ba8f19ec19914b73290c53a32de479cd463555 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 22:18:00 +0800 Subject: [PATCH 243/346] chore(deps): bump actions/checkout from 3 to 4 (#3712) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 2 +- .github/workflows/gin.yml | 4 ++-- .github/workflows/goreleaser.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e27022d109..b717a003fa 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 54d76bb430..645616bcb9 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -20,7 +20,7 @@ jobs: with: go-version: '^1.18' - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup golangci-lint uses: golangci/golangci-lint-action@v3.7.0 with: @@ -51,7 +51,7 @@ jobs: go-version: ${{ matrix.go }} - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.ref }} diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 07a054834e..406092665e 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - From a481ee2897af1e368de5c919fbeb21b89aa26fc7 Mon Sep 17 00:00:00 2001 From: Viral Parmar Date: Wed, 27 Sep 2023 12:47:11 +0530 Subject: [PATCH 244/346] chore(http): use white color for HTTP 1XX (#3741) --- logger.go | 2 ++ logger_test.go | 1 + 2 files changed, 3 insertions(+) diff --git a/logger.go b/logger.go index cd1e7fa6e9..1e6cf77a62 100644 --- a/logger.go +++ b/logger.go @@ -83,6 +83,8 @@ func (p *LogFormatterParams) StatusCodeColor() string { code := p.StatusCode switch { + case code >= http.StatusContinue && code < http.StatusOK: + return white case code >= http.StatusOK && code < http.StatusMultipleChoices: return green case code >= http.StatusMultipleChoices && code < http.StatusBadRequest: diff --git a/logger_test.go b/logger_test.go index 5f78708f1a..b93e1e0425 100644 --- a/logger_test.go +++ b/logger_test.go @@ -310,6 +310,7 @@ func TestColorForStatus(t *testing.T) { return p.StatusCodeColor() } + assert.Equal(t, white, colorForStatus(http.StatusContinue), "1xx should be white") assert.Equal(t, green, colorForStatus(http.StatusOK), "2xx should be green") assert.Equal(t, white, colorForStatus(http.StatusMovedPermanently), "3xx should be white") assert.Equal(t, yellow, colorForStatus(http.StatusNotFound), "4xx should be yellow") From bdde009dbbbae890db4e6ffdd252e2b4e63a1b85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:23:37 +0800 Subject: [PATCH 245/346] chore(deps): bump golang.org/x/net from 0.14.0 to 0.18.0 (#3774) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.14.0 to 0.18.0. - [Commits](https://github.com/golang/net/compare/v0.14.0...v0.18.0) --- updated-dependencies: - dependency-name: golang.org/x/net 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 | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 5c2ec05dfb..7ebadde71c 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.8 github.com/stretchr/testify v1.8.3 github.com/ugorji/go/codec v1.2.11 - golang.org/x/net v0.14.0 + golang.org/x/net v0.18.0 google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -30,7 +30,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index b992f59421..75848b480a 100644 --- a/go.sum +++ b/go.sum @@ -60,16 +60,16 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= From 0aeac86b05cd51c993304e7bcfc2e11cef025c83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 23:45:24 +0800 Subject: [PATCH 246/346] chore(deps): bump github.com/go-playground/validator/v10 from 10.15.1 to 10.16.0 (#3769) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.15.1 to 10.16.0. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.15.1...v10.16.0) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 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 7ebadde71c..c365e77ded 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/bytedance/sonic v1.9.1 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.15.1 + github.com/go-playground/validator/v10 v10.16.0 github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.19 diff --git a/go.sum b/go.sum index 75848b480a..4bd36dbbea 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM= -github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= +github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= From 49f45a542719df661bd71dd48f1595f0bc1ff6f7 Mon Sep 17 00:00:00 2001 From: WeiTheShinobi <43955151+WeiTheShinobi@users.noreply.github.com> Date: Thu, 16 Nov 2023 23:46:11 +0800 Subject: [PATCH 247/346] docs: remove redundant comments (#3765) --- gin.go | 1 - 1 file changed, 1 deletion(-) diff --git a/gin.go b/gin.go index ed8b6dad75..5a605cf143 100644 --- a/gin.go +++ b/gin.go @@ -334,7 +334,6 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { } root.addRoute(path, handlers) - // Update maxParams if paramsCount := countParams(path); paramsCount > engine.maxParams { engine.maxParams = paramsCount } From 44d0dd70924dd154e3b98bc340accc53484efa9c Mon Sep 17 00:00:00 2001 From: Omkar P <45419097+omkar-foss@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:16:43 +0530 Subject: [PATCH 248/346] fix: Add pointer support for url query params (#3659) (#3666) The pointer support in url query params (using []*Struct for binding query params) was previously available in Gin, but was removed in commit 0d50ce8 since there wasn't a test case for such a scenario, and so the case block was removed as a redundant one. --- binding/form_mapping.go | 5 +++++ binding/form_mapping_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 540bbbb84a..55435b9493 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -239,6 +239,11 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface()) case reflect.Map: return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface()) + case reflect.Ptr: + if !value.Elem().IsValid() { + value.Set(reflect.New(value.Type().Elem())) + } + return setWithProperType(val, value.Elem(), field) default: return errUnknownType } diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 93d6a92ff8..acea8f77a7 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -269,6 +269,39 @@ func TestMappingStructField(t *testing.T) { assert.Equal(t, 9, s.J.I) } +func TestMappingPtrField(t *testing.T) { + type ptrStruct struct { + Key int64 `json:"key"` + } + + type ptrRequest struct { + Items []*ptrStruct `json:"items" form:"items"` + } + + var err error + + // With 0 items. + var req0 ptrRequest + err = mappingByPtr(&req0, formSource{}, "form") + assert.NoError(t, err) + assert.Empty(t, req0.Items) + + // With 1 item. + var req1 ptrRequest + err = mappingByPtr(&req1, formSource{"items": {`{"key": 1}`}}, "form") + assert.NoError(t, err) + assert.Len(t, req1.Items, 1) + assert.EqualValues(t, 1, req1.Items[0].Key) + + // With 2 items. + var req2 ptrRequest + err = mappingByPtr(&req2, formSource{"items": {`{"key": 1}`, `{"key": 2}`}}, "form") + assert.NoError(t, err) + assert.Len(t, req2.Items, 2) + assert.EqualValues(t, 1, req2.Items[0].Key) + assert.EqualValues(t, 2, req2.Items[1].Key) +} + func TestMappingMapField(t *testing.T) { var s struct { M map[string]int From 386d244068db3693f938db4ead6d1f5f85942e3f Mon Sep 17 00:00:00 2001 From: Georgi Dimitrov <82881135+georgijd-form3@users.noreply.github.com> Date: Thu, 7 Dec 2023 00:38:55 +0000 Subject: [PATCH 249/346] fix(tree): correctly expand the capacity of params (#3502) --- routes_test.go | 39 +++++++++++++++++++++++++++++++++++++++ tree.go | 16 +++++++++++++++- tree_test.go | 34 +++++++++++++++++++++++++++++++--- 3 files changed, 85 insertions(+), 4 deletions(-) diff --git a/routes_test.go b/routes_test.go index 633c0aba14..7a51f81771 100644 --- a/routes_test.go +++ b/routes_test.go @@ -337,6 +337,45 @@ func TestRouteParamsByNameWithExtraSlash(t *testing.T) { assert.Equal(t, "/is/super/great", wild) } +// TestRouteParamsNotEmpty tests that context parameters will be set +// even if a route with params/wildcards is registered after the context +// initialisation (which happened in a previous requets). +func TestRouteParamsNotEmpty(t *testing.T) { + name := "" + lastName := "" + wild := "" + router := New() + + w := PerformRequest(router, http.MethodGet, "/test/john/smith/is/super/great") + + assert.Equal(t, http.StatusNotFound, w.Code) + + router.GET("/test/:name/:last_name/*wild", func(c *Context) { + name = c.Params.ByName("name") + lastName = c.Params.ByName("last_name") + var ok bool + wild, ok = c.Params.Get("wild") + + assert.True(t, ok) + assert.Equal(t, name, c.Param("name")) + assert.Equal(t, lastName, c.Param("last_name")) + + assert.Empty(t, c.Param("wtf")) + assert.Empty(t, c.Params.ByName("wtf")) + + wtf, ok := c.Params.Get("wtf") + assert.Empty(t, wtf) + assert.False(t, ok) + }) + + w = PerformRequest(router, http.MethodGet, "/test/john/smith/is/super/great") + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "john", name) + assert.Equal(t, "smith", lastName) + assert.Equal(t, "/is/super/great", wild) +} + // TestHandleStaticFile - ensure the static file handles properly func TestRouteStaticFile(t *testing.T) { // SETUP file diff --git a/tree.go b/tree.go index dda8f4f7b0..7b1e008b99 100644 --- a/tree.go +++ b/tree.go @@ -497,7 +497,14 @@ walk: // Outer loop for walking the tree } // Save param value - if params != nil && cap(*params) > 0 { + if params != nil { + // Preallocate capacity if necessary + if cap(*params) < int(globalParamsCount) { + newParams := make(Params, len(*params), globalParamsCount) + copy(newParams, *params) + *params = newParams + } + if value.params == nil { value.params = params } @@ -544,6 +551,13 @@ walk: // Outer loop for walking the tree case catchAll: // Save param value if params != nil { + // Preallocate capacity if necessary + if cap(*params) < int(globalParamsCount) { + newParams := make(Params, len(*params), globalParamsCount) + copy(newParams, *params) + *params = newParams + } + if value.params == nil { value.params = params } diff --git a/tree_test.go b/tree_test.go index 2005738ec6..aacc914cf9 100644 --- a/tree_test.go +++ b/tree_test.go @@ -893,9 +893,9 @@ func TestTreeInvalidNodeType(t *testing.T) { func TestTreeInvalidParamsType(t *testing.T) { tree := &node{} - tree.wildChild = true - tree.children = append(tree.children, &node{}) - tree.children[0].nType = 2 + // add a child with wildcard + route := "/:path" + tree.addRoute(route, fakeHandler(route)) // set invalid Params type params := make(Params, 0) @@ -904,6 +904,34 @@ func TestTreeInvalidParamsType(t *testing.T) { tree.getValue("/test", ¶ms, getSkippedNodes(), false) } +func TestTreeExpandParamsCapacity(t *testing.T) { + data := []struct { + path string + }{ + {"/:path"}, + {"/*path"}, + } + + for _, item := range data { + tree := &node{} + tree.addRoute(item.path, fakeHandler(item.path)) + params := make(Params, 0) + + value := tree.getValue("/test", ¶ms, getSkippedNodes(), false) + + if value.params == nil { + t.Errorf("Expected %s params to be set, but they weren't", item.path) + continue + } + + if len(*value.params) != 1 { + t.Errorf("Wrong number of %s params: got %d, want %d", + item.path, len(*value.params), 1) + continue + } + } +} + func TestTreeWildcardConflictEx(t *testing.T) { conflicts := [...]struct { route string From 081b36ebdbf3635143dacd36b92e48529a98e34e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Dec 2023 10:27:25 +0800 Subject: [PATCH 250/346] chore(deps): bump actions/setup-go from 4 to 5 (#3798) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gin.yml | 4 ++-- .github/workflows/goreleaser.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 645616bcb9..5f8c0c0821 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Setup go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.18' - name: Checkout repository @@ -46,7 +46,7 @@ jobs: GOPROXY: https://proxy.golang.org steps: - name: Set up Go ${{ matrix.go }} - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 406092665e..5364a9138d 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -19,7 +19,7 @@ jobs: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: 1.20 - From 811f271a0491b3d74ce3c9948c10f95fe6f64206 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Dec 2023 10:27:57 +0800 Subject: [PATCH 251/346] chore(deps): bump goreleaser/goreleaser-action from 4 to 5 (#3721) Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 4 to 5. - [Release notes](https://github.com/goreleaser/goreleaser-action/releases) - [Commits](https://github.com/goreleaser/goreleaser-action/compare/v4...v5) --- updated-dependencies: - dependency-name: goreleaser/goreleaser-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 5364a9138d..0180323237 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -24,7 +24,7 @@ jobs: go-version: 1.20 - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v4 + uses: goreleaser/goreleaser-action@v5 with: # either 'goreleaser' (default) or 'goreleaser-pro' distribution: goreleaser From 53fbf4dbfbf465b552057e6f8d199a275151b7a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Dec 2023 10:28:51 +0800 Subject: [PATCH 252/346] chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.8 to 2.1.1 (#3797) Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.8 to 2.1.1. - [Release notes](https://github.com/pelletier/go-toml/releases) - [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml) - [Commits](https://github.com/pelletier/go-toml/compare/v2.0.8...v2.1.1) --- updated-dependencies: - dependency-name: github.com/pelletier/go-toml/v2 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 | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c365e77ded..6b348fe2c6 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,8 @@ require ( github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.19 - github.com/pelletier/go-toml/v2 v2.0.8 - github.com/stretchr/testify v1.8.3 + github.com/pelletier/go-toml/v2 v2.1.1 + github.com/stretchr/testify v1.8.4 github.com/ugorji/go/codec v1.2.11 golang.org/x/net v0.18.0 google.golang.org/protobuf v1.30.0 diff --git a/go.sum b/go.sum index 4bd36dbbea..39d74fc9a5 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -51,8 +51,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= From 160c1730efd30046239c802d5b9f895a708c3f4c Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 19 Jan 2024 00:35:08 +0800 Subject: [PATCH 253/346] chore: update GitHub Actions configuration (#3792) - Change the cron schedule from `'0 17 * * 5'` to `"0 17 * * 5"` in the file `.github/workflows/codeql.yml` - Change the value of `language` from `['go']` to `["go"]` in the file `.github/workflows/codeql.yml` - Change the value of `go-version` from `'^1.18'` to `"^1.18"` in the file `.github/workflows/gin.yml` - Add `1.21` to the list of `go` versions and change the value of `test-tags` in the file `.github/workflows/gin.yml` - Change the value of `if` condition from `matrix.go-version == '1.20.x'` to `matrix.go-version == '1.21.x'` in the file `.github/workflows/gin.yml` - Change the value of `on` from `'*'` to `"*"` in the file `.github/workflows/goreleaser.yml` - Change the name of the job from `name: Checkout` to `name: Checkout` in the file `.github/workflows/goreleaser.yml` - Change the name of the job from `name: Set up Go` to `name: Set up Go` in the file `.github/workflows/goreleaser.yml` - Change the value of `go-version` from `1.20` to `"^1"` in Signed-off-by: Bo-Yi Wu --- .github/workflows/codeql.yml | 8 ++++---- .github/workflows/gin.yml | 8 ++++---- .github/workflows/goreleaser.yml | 7 +++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b717a003fa..858124e184 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -7,12 +7,12 @@ name: "CodeQL" on: push: - branches: [ master ] + branches: [master] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: [master] schedule: - - cron: '0 17 * * 5' + - cron: "0 17 * * 5" jobs: analyze: @@ -29,7 +29,7 @@ jobs: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] # TODO: Enable for javascript later - language: [ 'go'] + language: ["go"] steps: - name: Checkout repository diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 5f8c0c0821..2149a21ec9 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -18,7 +18,7 @@ jobs: - name: Setup go uses: actions/setup-go@v5 with: - go-version: '^1.18' + go-version: "^1.18" - name: Checkout repository uses: actions/checkout@v4 - name: Setup golangci-lint @@ -31,8 +31,8 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: ['1.18', '1.19', '1.20'] - test-tags: ['', '-tags nomsgpack', '-tags "sonic avx"', '-tags go_json'] + go: ["1.18", "1.19", "1.20", "1.21"] + test-tags: ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json"] include: - os: ubuntu-latest go-build: ~/.cache/go-build @@ -73,5 +73,5 @@ jobs: flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }} - name: Format - if: matrix.go-version == '1.20.x' + if: matrix.go-version == '1.21.x' run: diff -u <(echo -n) <(gofmt -d .) diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 0180323237..cbd5d418c1 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -3,7 +3,7 @@ name: Goreleaser on: push: tags: - - '*' + - "*" permissions: contents: write @@ -12,8 +12,7 @@ jobs: goreleaser: runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 @@ -21,7 +20,7 @@ jobs: name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.20 + go-version: "^1" - name: Run GoReleaser uses: goreleaser/goreleaser-action@v5 From 857db39f82fb82456af2906ccea972ae1d65ff57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:18:57 +0800 Subject: [PATCH 254/346] chore(deps): bump github/codeql-action from 2 to 3 (#3806) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 858124e184..9a4c40d77e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -37,7 +37,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -46,4 +46,4 @@ jobs: # queries: ./path/to/local/query, your-org/your-repo/queries@main - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 4a40f8f1a49b9086b461d97e167c3b9628d8b923 Mon Sep 17 00:00:00 2001 From: caption <101684156+chncaption@users.noreply.github.com> Date: Thu, 1 Feb 2024 09:00:17 +0800 Subject: [PATCH 255/346] fix(sec): upgrade golang.org/x/crypto to 0.17.0 (#3832) --- go.mod | 4 ++-- go.sum | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 6b348fe2c6..7f560453c0 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.15.0 // indirect - golang.org/x/sys v0.14.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index 39d74fc9a5..f4bf8ff0d1 100644 --- a/go.sum +++ b/go.sum @@ -62,12 +62,15 @@ golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= From 8ab47c694ea93fdb442b617961ce9b3171151749 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 1 Feb 2024 11:03:26 +0800 Subject: [PATCH 256/346] ci(lint): update tooling and workflows for consistency (#3834) * chore: update tooling and workflows for consistency - Update the version of a tool in the GitHub workflow from `v1.52.2` to `v1.55.2` Signed-off-by: Bo-Yi Wu * chore: refactor linter configuration in CI - Remove the `depguard` linter from the `.golangci.yml` configuration Signed-off-by: Bo-Yi Wu * ci: refine CI workflow and test configurations - Disable caching in the GitHub Actions workflow for `gin.yml` Signed-off-by: Bo-Yi Wu * refactor: refactor return logic in tree operations - Modify multiple return statements in `tree.go` to return a specific value instead of nothing Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu --- .github/workflows/gin.yml | 3 ++- .golangci.yml | 1 - go.sum | 5 +---- tree.go | 22 +++++++++++----------- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 2149a21ec9..4ec954353e 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -24,7 +24,7 @@ jobs: - name: Setup golangci-lint uses: golangci/golangci-lint-action@v3.7.0 with: - version: v1.52.2 + version: v1.55.2 args: --verbose test: needs: lint @@ -49,6 +49,7 @@ jobs: uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} + cache: false - name: Checkout Code uses: actions/checkout@v4 diff --git a/.golangci.yml b/.golangci.yml index 91dae02c39..4a72f7342f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,7 +3,6 @@ run: linters: enable: - asciicheck - - depguard - dogsled - durationcheck - errcheck diff --git a/go.sum b/go.sum index f4bf8ff0d1..8c4ef3fb00 100644 --- a/go.sum +++ b/go.sum @@ -60,16 +60,13 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/tree.go b/tree.go index 7b1e008b99..456464618b 100644 --- a/tree.go +++ b/tree.go @@ -478,7 +478,7 @@ walk: // Outer loop for walking the tree // We can recommend to redirect to the same URL without a // trailing slash if a leaf exists for that path. value.tsr = path == "/" && n.handlers != nil - return + return value } // Handle wildcard child, which is always at the end of the array @@ -533,12 +533,12 @@ walk: // Outer loop for walking the tree // ... but we can't value.tsr = len(path) == end+1 - return + return value } if value.handlers = n.handlers; value.handlers != nil { value.fullPath = n.fullPath - return + return value } if len(n.children) == 1 { // No handle found. Check if a handle for this path + a @@ -546,7 +546,7 @@ walk: // Outer loop for walking the tree n = n.children[0] value.tsr = (n.path == "/" && n.handlers != nil) || (n.path == "" && n.indices == "/") } - return + return value case catchAll: // Save param value @@ -578,7 +578,7 @@ walk: // Outer loop for walking the tree value.handlers = n.handlers value.fullPath = n.fullPath - return + return value default: panic("invalid node type") @@ -609,7 +609,7 @@ walk: // Outer loop for walking the tree // Check if this node has a handle registered. if value.handlers = n.handlers; value.handlers != nil { value.fullPath = n.fullPath - return + return value } // If there is no handle for this route, but this route has a @@ -617,12 +617,12 @@ walk: // Outer loop for walking the tree // additional trailing slash if path == "/" && n.wildChild && n.nType != root { value.tsr = true - return + return value } if path == "/" && n.nType == static { value.tsr = true - return + return value } // No handle found. Check if a handle for this path + a @@ -632,11 +632,11 @@ walk: // Outer loop for walking the tree n = n.children[i] value.tsr = (len(n.path) == 1 && n.handlers != nil) || (n.nType == catchAll && n.children[0].handlers != nil) - return + return value } } - return + return value } // Nothing found. We can recommend to redirect to the same URL with an @@ -662,7 +662,7 @@ walk: // Outer loop for walking the tree } } - return + return value } } From a64286a7760be2031209686ce4d36e99d42dd419 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 1 Feb 2024 12:17:36 +0800 Subject: [PATCH 257/346] chore(deps): update dependencies to latest versions (#3835) * chore: update dependencies to latest versions - Update `sonic` library from `v1.9.1` to `v1.10.2` - Update `validator` library from `v10.16.0` to `v10.17.0` - Update `go-isatty` library from `v0.0.19` to `v0.0.20` - Update `go/codec`, `x/net`, and `protobuf` libraries to newer versions - Update `base64x` to a newer commit and add `iasm` library as an indirect dependency - Update `mimetype`, `cpuid`, `go-urn`, `x/arch`, `x/crypto`, and `x/sys` libraries to newer versions Signed-off-by: Bo-Yi Wu * ci: refactor CI workflows and improve robustness - Update GitHub Actions cache from v3 to v4 in the workflow configuration Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu --- .github/workflows/gin.yml | 2 +- go.mod | 27 +++++++++-------- go.sum | 64 ++++++++++++++++++++------------------- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 4ec954353e..b36e10102e 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -56,7 +56,7 @@ jobs: with: ref: ${{ github.ref }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: | ${{ matrix.go-build }} diff --git a/go.mod b/go.mod index 7f560453c0..0b60c5d73b 100644 --- a/go.mod +++ b/go.mod @@ -3,34 +3,35 @@ module github.com/gin-gonic/gin go 1.20 require ( - github.com/bytedance/sonic v1.9.1 + github.com/bytedance/sonic v1.10.2 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.16.0 + github.com/go-playground/validator/v10 v10.17.0 github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 - github.com/mattn/go-isatty v0.0.19 + github.com/mattn/go-isatty v0.0.20 github.com/pelletier/go-toml/v2 v2.1.1 github.com/stretchr/testify v1.8.4 - github.com/ugorji/go/codec v1.2.11 - golang.org/x/net v0.18.0 - google.golang.org/protobuf v1.30.0 + github.com/ugorji/go/codec v1.2.12 + golang.org/x/net v0.20.0 + google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/leodido/go-urn v1.3.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/arch v0.7.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index 8c4ef3fb00..e360d9d28b 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,19 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= +github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= +github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -16,23 +21,22 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= -github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= +github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/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= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.3.0 h1:jX8FDLfW4ThVXctBNZ+3cIWnCSnrACDV73r76dy0aQQ= +github.com/leodido/go-urn v1.3.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -50,34 +54,32 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= +golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From c6ae2e69666a2b36203b29650ee75d172c725c66 Mon Sep 17 00:00:00 2001 From: Ghobad Date: Fri, 2 Feb 2024 05:22:26 +0330 Subject: [PATCH 258/346] feat(logger): ability to skip logs based on user-defined logic (#3593) * log skipper * do not call time.now() if logging should be skipped * do not ignore skip func delay in latency calculation * write docs * write test --- docs/doc.md | 38 +++++++++++++++++++++++++++++++++++++ logger.go | 51 +++++++++++++++++++++++++++++--------------------- logger_test.go | 20 ++++++++++++++++++++ 3 files changed, 88 insertions(+), 21 deletions(-) diff --git a/docs/doc.md b/docs/doc.md index e48c2ba183..520d105c04 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -508,6 +508,44 @@ Sample Output ::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" " ``` +### Skip logging + +```go +func main() { + router := gin.New() + + // skip logging for desired paths by setting SkipPaths in LoggerConfig + loggerConfig := gin.LoggerConfig{SkipPaths: []string{"/metrics"}} + + // skip logging based on your logic by setting Skip func in LoggerConfig + loggerConfig.Skip = func(c *gin.Context) bool { + // as an example skip non server side errors + return c.Writer.Status() < http.StatusInternalServerError + } + + engine.Use(gin.LoggerWithConfig(loggerConfig)) + router.Use(gin.Recovery()) + + // skipped + router.GET("/metrics", func(c *gin.Context) { + c.Status(http.StatusNotImplemented) + }) + + // skipped + router.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "pong") + }) + + // not skipped + router.GET("/data", func(c *gin.Context) { + c.Status(http.StatusNotImplemented) + }) + + router.Run(":8080") +} + +``` + ### Controlling Log output coloring By default, logs output on console should be colorized depending on the detected TTY. diff --git a/logger.go b/logger.go index 1e6cf77a62..db2c6832b2 100644 --- a/logger.go +++ b/logger.go @@ -47,8 +47,15 @@ type LoggerConfig struct { // SkipPaths is an url path array which logs are not written. // Optional. SkipPaths []string + + // Skip is a Skipper that indicates which logs should not be written. + // Optional. + Skip Skipper } +// Skipper is a function to skip logs based on provided Context +type Skipper func(c *Context) bool + // LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter type LogFormatter func(params LogFormatterParams) string @@ -241,32 +248,34 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc { // Process request c.Next() - // Log only when path is not being skipped - if _, ok := skip[path]; !ok { - param := LogFormatterParams{ - Request: c.Request, - isTerm: isTerm, - Keys: c.Keys, - } - - // Stop timer - param.TimeStamp = time.Now() - param.Latency = param.TimeStamp.Sub(start) + // Log only when it is not being skipped + if _, ok := skip[path]; ok || (conf.Skip != nil && conf.Skip(c)) { + return + } - param.ClientIP = c.ClientIP() - param.Method = c.Request.Method - param.StatusCode = c.Writer.Status() - param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String() + param := LogFormatterParams{ + Request: c.Request, + isTerm: isTerm, + Keys: c.Keys, + } - param.BodySize = c.Writer.Size() + // Stop timer + param.TimeStamp = time.Now() + param.Latency = param.TimeStamp.Sub(start) - if raw != "" { - path = path + "?" + raw - } + param.ClientIP = c.ClientIP() + param.Method = c.Request.Method + param.StatusCode = c.Writer.Status() + param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String() - param.Path = path + param.BodySize = c.Writer.Size() - fmt.Fprint(out, formatter(param)) + if raw != "" { + path = path + "?" + raw } + + param.Path = path + + fmt.Fprint(out, formatter(param)) } } diff --git a/logger_test.go b/logger_test.go index b93e1e0425..6c1814dc11 100644 --- a/logger_test.go +++ b/logger_test.go @@ -415,6 +415,26 @@ func TestLoggerWithConfigSkippingPaths(t *testing.T) { assert.Contains(t, buffer.String(), "") } +func TestLoggerWithConfigSkipper(t *testing.T) { + buffer := new(strings.Builder) + router := New() + router.Use(LoggerWithConfig(LoggerConfig{ + Output: buffer, + Skip: func(c *Context) bool { + return c.Writer.Status() == http.StatusNoContent + }, + })) + router.GET("/logged", func(c *Context) { c.Status(http.StatusOK) }) + router.GET("/skipped", func(c *Context) { c.Status(http.StatusNoContent) }) + + PerformRequest(router, "GET", "/logged") + assert.Contains(t, buffer.String(), "200") + + buffer.Reset() + PerformRequest(router, "GET", "/skipped") + assert.Contains(t, buffer.String(), "") +} + func TestDisableConsoleColor(t *testing.T) { New() assert.Equal(t, autoColor, consoleColorMode) From 9f598a31aafb92d675f38f1c8371e4ac76f858bf Mon Sep 17 00:00:00 2001 From: Prakhar Gurunani Date: Sun, 4 Feb 2024 18:44:29 +0530 Subject: [PATCH 259/346] fix(router): catch-all conflicting wildcard (#3812) * fix: catch-all conflicting wildcard * add: test cases * chore: update GitHub Actions configuration (#3792) - Change the cron schedule from `'0 17 * * 5'` to `"0 17 * * 5"` in the file `.github/workflows/codeql.yml` - Change the value of `language` from `['go']` to `["go"]` in the file `.github/workflows/codeql.yml` - Change the value of `go-version` from `'^1.18'` to `"^1.18"` in the file `.github/workflows/gin.yml` - Add `1.21` to the list of `go` versions and change the value of `test-tags` in the file `.github/workflows/gin.yml` - Change the value of `if` condition from `matrix.go-version == '1.20.x'` to `matrix.go-version == '1.21.x'` in the file `.github/workflows/gin.yml` - Change the value of `on` from `'*'` to `"*"` in the file `.github/workflows/goreleaser.yml` - Change the name of the job from `name: Checkout` to `name: Checkout` in the file `.github/workflows/goreleaser.yml` - Change the name of the job from `name: Set up Go` to `name: Set up Go` in the file `.github/workflows/goreleaser.yml` - Change the value of `go-version` from `1.20` to `"^1"` in Signed-off-by: Bo-Yi Wu * chore(deps): bump github/codeql-action from 2 to 3 (#3806) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(sec): upgrade golang.org/x/crypto to 0.17.0 (#3832) * ci(lint): update tooling and workflows for consistency (#3834) * chore: update tooling and workflows for consistency - Update the version of a tool in the GitHub workflow from `v1.52.2` to `v1.55.2` Signed-off-by: Bo-Yi Wu * chore: refactor linter configuration in CI - Remove the `depguard` linter from the `.golangci.yml` configuration Signed-off-by: Bo-Yi Wu * ci: refine CI workflow and test configurations - Disable caching in the GitHub Actions workflow for `gin.yml` Signed-off-by: Bo-Yi Wu * refactor: refactor return logic in tree operations - Modify multiple return statements in `tree.go` to return a specific value instead of nothing Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu * chore(deps): update dependencies to latest versions (#3835) * chore: update dependencies to latest versions - Update `sonic` library from `v1.9.1` to `v1.10.2` - Update `validator` library from `v10.16.0` to `v10.17.0` - Update `go-isatty` library from `v0.0.19` to `v0.0.20` - Update `go/codec`, `x/net`, and `protobuf` libraries to newer versions - Update `base64x` to a newer commit and add `iasm` library as an indirect dependency - Update `mimetype`, `cpuid`, `go-urn`, `x/arch`, `x/crypto`, and `x/sys` libraries to newer versions Signed-off-by: Bo-Yi Wu * ci: refactor CI workflows and improve robustness - Update GitHub Actions cache from v3 to v4 in the workflow configuration Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu * wip: fix tests * wip: fix tests --------- Signed-off-by: Bo-Yi Wu Signed-off-by: dependabot[bot] Co-authored-by: Bo-Yi Wu Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: caption <101684156+chncaption@users.noreply.github.com> --- tree.go | 5 ++++- tree_test.go | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 456464618b..878023d1cf 100644 --- a/tree.go +++ b/tree.go @@ -351,7 +351,10 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) } if len(n.path) > 0 && n.path[len(n.path)-1] == '/' { - pathSeg := strings.SplitN(n.children[0].path, "/", 2)[0] + pathSeg := "" + if len(n.children) != 0 { + pathSeg = strings.SplitN(n.children[0].path, "/", 2)[0] + } panic("catch-all wildcard '" + path + "' in new path '" + fullPath + "' conflicts with existing path segment '" + pathSeg + diff --git a/tree_test.go b/tree_test.go index aacc914cf9..c9b03130b6 100644 --- a/tree_test.go +++ b/tree_test.go @@ -417,6 +417,8 @@ func TestTreeWildcardConflict(t *testing.T) { {"/user_:name", false}, {"/id:id", false}, {"/id/:id", false}, + {"/static/*file", false}, + {"/static/", true}, } testRoutes(t, routes) } From 3dc1cd6572b4e3a0cd170a15debe546c2c72294f Mon Sep 17 00:00:00 2001 From: clearcode <34591322+clearcodecn@users.noreply.github.com> Date: Mon, 5 Feb 2024 10:46:35 +0800 Subject: [PATCH 260/346] fix(binding): binding error while not upload file (#3819) (#3820) Co-authored-by: zhangmj --- binding/form_mapping.go | 3 +++ binding/form_mapping_test.go | 2 ++ 2 files changed, 5 insertions(+) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 55435b9493..77a1bde697 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -7,6 +7,7 @@ package binding import ( "errors" "fmt" + "mime/multipart" "reflect" "strconv" "strings" @@ -235,6 +236,8 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel switch value.Interface().(type) { case time.Time: return setTimeField(val, field, value) + case multipart.FileHeader: + return nil } return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface()) case reflect.Map: diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index acea8f77a7..16527eb916 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -5,6 +5,7 @@ package binding import ( + "mime/multipart" "reflect" "testing" "time" @@ -43,6 +44,7 @@ func TestMappingBaseTypes(t *testing.T) { {"zero value", struct{ F uint }{}, "", uint(0)}, {"zero value", struct{ F bool }{}, "", false}, {"zero value", struct{ F float32 }{}, "", float32(0)}, + {"file value", struct{ F *multipart.FileHeader }{}, "", &multipart.FileHeader{}}, } { tp := reflect.TypeOf(tt.value) testName := tt.name + ":" + tp.Field(0).Type.String() From e957d1abf13846e458956d8c97e7b7c76c7ee9a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:39:24 +0800 Subject: [PATCH 261/346] chore(deps): bump codecov/codecov-action from 3 to 4 (#3838) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 b36e10102e..75e6d05d91 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -69,7 +69,7 @@ jobs: run: make test - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }} From 86ff4a64c7efe1a1c875529835eeef9e15de1e86 Mon Sep 17 00:00:00 2001 From: Gabriel Augendre Date: Tue, 6 Feb 2024 04:08:56 +0100 Subject: [PATCH 262/346] fix(header): Allow header according to RFC 7231 (HTTP 405) (#3759) Co-authored-by: Helios --- gin.go | 14 +++++++++++--- routes_test.go | 12 ++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/gin.go b/gin.go index 5a605cf143..b6ac5353b0 100644 --- a/gin.go +++ b/gin.go @@ -633,17 +633,25 @@ func (engine *Engine) handleHTTPRequest(c *Context) { } if engine.HandleMethodNotAllowed { + // According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response + // containing a list of the target resource's currently supported methods. + allowed := make([]string, 0, len(t)-1) for _, tree := range engine.trees { if tree.method == httpMethod { continue } if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil { - c.handlers = engine.allNoMethod - serveError(c, http.StatusMethodNotAllowed, default405Body) - return + allowed = append(allowed, tree.method) } } + if len(allowed) > 0 { + c.handlers = engine.allNoMethod + c.writermem.Header().Set("Allow", strings.Join(allowed, ", ")) + serveError(c, http.StatusMethodNotAllowed, default405Body) + return + } } + c.handlers = engine.allNoRoute serveError(c, http.StatusNotFound, default404Body) } diff --git a/routes_test.go b/routes_test.go index 7a51f81771..a0ff695fd9 100644 --- a/routes_test.go +++ b/routes_test.go @@ -514,6 +514,18 @@ func TestRouteNotAllowedEnabled2(t *testing.T) { assert.Equal(t, http.StatusMethodNotAllowed, w.Code) } +func TestRouteNotAllowedEnabled3(t *testing.T) { + router := New() + router.HandleMethodNotAllowed = true + router.GET("/path", func(c *Context) {}) + router.POST("/path", func(c *Context) {}) + w := PerformRequest(router, http.MethodPut, "/path") + assert.Equal(t, http.StatusMethodNotAllowed, w.Code) + allowed := w.Header().Get("Allow") + assert.Contains(t, allowed, "GET") + assert.Contains(t, allowed, "POST") +} + func TestRouteNotAllowedDisabled(t *testing.T) { router := New() router.HandleMethodNotAllowed = false From 82bcd6d39bfe9c22032764ff3b0b6f8ef1673e49 Mon Sep 17 00:00:00 2001 From: Alonso Villegas Date: Wed, 7 Feb 2024 06:44:11 -0500 Subject: [PATCH 263/346] fix(binding): dereference pointer to struct (#3199) --- binding/default_validator.go | 5 ++++- binding/validate_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/binding/default_validator.go b/binding/default_validator.go index e216b8546c..ac43d7cc5e 100644 --- a/binding/default_validator.go +++ b/binding/default_validator.go @@ -54,7 +54,10 @@ func (v *defaultValidator) ValidateStruct(obj any) error { value := reflect.ValueOf(obj) switch value.Kind() { case reflect.Ptr: - return v.ValidateStruct(value.Elem().Interface()) + if value.Elem().Kind() != reflect.Struct { + return v.ValidateStruct(value.Elem().Interface()) + } + return v.validateStruct(obj) case reflect.Struct: return v.validateStruct(obj) case reflect.Slice, reflect.Array: diff --git a/binding/validate_test.go b/binding/validate_test.go index 801bd9b7c5..1fc15ff079 100644 --- a/binding/validate_test.go +++ b/binding/validate_test.go @@ -192,6 +192,30 @@ func TestValidatePrimitives(t *testing.T) { assert.Equal(t, "value", str) } +type structModifyValidation struct { + Integer int +} + +func toZero(sl validator.StructLevel) { + var s *structModifyValidation = sl.Top().Interface().(*structModifyValidation) + s.Integer = 0 +} + +func TestValidateAndModifyStruct(t *testing.T) { + // This validates that pointers to structs are passed to the validator + // giving us the ability to modify the struct being validated. + engine, ok := Validator.Engine().(*validator.Validate) + assert.True(t, ok) + + engine.RegisterStructValidation(toZero, structModifyValidation{}) + + s := structModifyValidation{Integer: 1} + errs := validate(&s) + + assert.Nil(t, errs) + assert.Equal(t, s, structModifyValidation{Integer: 0}) +} + // structCustomValidation is a helper struct we use to check that // custom validation can be registered on it. // The `notone` binding directive is for custom validation and registered later. From bb3519d26f52835cf00e5e430b52651a9c378c97 Mon Sep 17 00:00:00 2001 From: Andy Brody Date: Wed, 7 Feb 2024 07:18:53 -0500 Subject: [PATCH 264/346] chore(IP): add TrustedPlatform constant for Fly.io. (#3839) Also add some more detail to the docs for how to use TrustedPlatform. https://fly.io/docs/reference/runtime-environment/#fly-client-ip --- context_test.go | 7 +++++++ docs/doc.md | 12 +++++++++--- gin.go | 2 ++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/context_test.go b/context_test.go index 70d4758377..88165c07c9 100644 --- a/context_test.go +++ b/context_test.go @@ -1569,6 +1569,12 @@ func TestContextClientIP(t *testing.T) { c.Request.Header.Del("CF-Connecting-IP") assert.Equal(t, "40.40.40.40", c.ClientIP()) + c.engine.TrustedPlatform = PlatformFlyIO + assert.Equal(t, "70.70.70.70", c.ClientIP()) + + c.Request.Header.Del("Fly-Client-IP") + assert.Equal(t, "40.40.40.40", c.ClientIP()) + c.engine.TrustedPlatform = "" // no port @@ -1581,6 +1587,7 @@ func resetContextForClientIPTests(c *Context) { 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("CF-Connecting-IP", "60.60.60.60") + c.Request.Header.Set("Fly-Client-IP", "70.70.70.70") c.Request.RemoteAddr = " 40.40.40.40:42123 " c.engine.TrustedPlatform = "" c.engine.trustedCIDRs = defaultTrustedCIDRs diff --git a/docs/doc.md b/docs/doc.md index 520d105c04..df006e87a7 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -2214,10 +2214,16 @@ import ( func main() { router := gin.Default() // Use predefined header gin.PlatformXXX + // Google App Engine router.TrustedPlatform = gin.PlatformGoogleAppEngine - // Or set your own trusted request header for another trusted proxy service - // Don't set it to any suspect request header, it's unsafe - router.TrustedPlatform = "X-CDN-IP" + // Cloudflare + router.TrustedPlatform = gin.PlatformCloudflare + // Fly.io + router.TrustedPlatform = gin.PlatformFlyIO + // Or, you can set your own trusted request header. But be sure your CDN + // prevents users from passing this header! For example, if your CDN puts + // the client IP in X-CDN-Client-IP: + router.TrustedPlatform = "X-CDN-Client-IP" router.GET("/", func(c *gin.Context) { // If you set TrustedPlatform, ClientIP() will resolve the diff --git a/gin.go b/gin.go index b6ac5353b0..24a9864aff 100644 --- a/gin.go +++ b/gin.go @@ -77,6 +77,8 @@ const ( // PlatformCloudflare when using Cloudflare's CDN. Trust CF-Connecting-IP for determining // the client's IP PlatformCloudflare = "CF-Connecting-IP" + // PlatformFlyIO when running on Fly.io. Trust Fly-Client-IP for determining the client's IP + PlatformFlyIO = "Fly-Client-IP" ) // Engine is the framework's instance, it contains the muxer, middleware and configuration settings. From 000fdb3ac95c7c318440afbd98eaf60f7430a1db Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 18 Feb 2024 17:32:06 +0800 Subject: [PATCH 265/346] ci(testing): add go1.22 version (#3842) * chore: update gin version and remove unnecessary steps in GitHub workflows - Update the version of gin to v1.56.1 in the `.github/workflows/gin.yml` file - Add go version 1.22 to the list of supported versions in the `.github/workflows/gin.yml` file - Remove the unnecessary step "Set up Go" in the `.github/workflows/goreleaser.yml` file - Update the step name "Run GoReleaser" in the `.github/workflows/goreleaser.yml` file Signed-off-by: appleboy * ci: update dependencies and CI configurations - Update conditional Go version check in GitHub Actions workflow from `1.21.x` to `1.22.x` Signed-off-by: Bo-Yi Wu * ci: improve CI Robustness and Test Reliability - Add `-race` flag to the test-tags list in GitHub Actions workflow configuration Signed-off-by: Bo-Yi Wu --------- Signed-off-by: appleboy Signed-off-by: Bo-Yi Wu --- .github/workflows/gin.yml | 9 +++++---- .github/workflows/goreleaser.yml | 6 ++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 75e6d05d91..9ab00ae3ad 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -24,15 +24,16 @@ jobs: - name: Setup golangci-lint uses: golangci/golangci-lint-action@v3.7.0 with: - version: v1.55.2 + version: v1.56.1 args: --verbose test: needs: lint strategy: matrix: os: [ubuntu-latest, macos-latest] - go: ["1.18", "1.19", "1.20", "1.21"] - test-tags: ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json"] + go: ["1.18", "1.19", "1.20", "1.21", "1.22"] + test-tags: + ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json", "-race"] include: - os: ubuntu-latest go-build: ~/.cache/go-build @@ -74,5 +75,5 @@ jobs: flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }} - name: Format - if: matrix.go-version == '1.21.x' + if: matrix.go-version == '1.22.x' run: diff -u <(echo -n) <(gofmt -d .) diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index cbd5d418c1..8ae1182330 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -16,13 +16,11 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - - name: Set up Go + - name: Set up Go uses: actions/setup-go@v5 with: go-version: "^1" - - - name: Run GoReleaser + - name: Run GoReleaser uses: goreleaser/goreleaser-action@v5 with: # either 'goreleaser' (default) or 'goreleaser-pro' From ecdbbbe9483dd12222f2085f717a2c7cb5ac55fe Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 19 Feb 2024 10:34:48 +0800 Subject: [PATCH 266/346] chore: refactor CI and update dependencies (#3848) - Update GitHub Actions workflow to use a unified step for checking out the repository and setting up Go with dynamic versioning - Upgrade golangci-lint-action version from v3.7.0 to v4 and bump the lint version from v1.56.1 to v1.56.2 - Update dependencies in go.mod: sonic from v1.10.2 to v1.11.0, validator from v10.17.0 to v10.18.0, x/net from v0.20.0 to v0.21.0, go-urn from v1.3.0 to v1.4.0, x/crypto from v0.18.0 to v0.19.0, and x/sys from v0.16.0 to v0.17.0 Signed-off-by: Bo-Yi Wu --- .github/workflows/gin.yml | 15 +++++++++------ go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 9ab00ae3ad..3fe007f16a 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -15,16 +15,19 @@ jobs: lint: runs-on: ubuntu-latest steps: - - name: Setup go + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "^1.18" - - name: Checkout repository - uses: actions/checkout@v4 + go-version-file: "go.mod" + check-latest: true - name: Setup golangci-lint - uses: golangci/golangci-lint-action@v3.7.0 + uses: golangci/golangci-lint-action@v4 with: - version: v1.56.1 + version: v1.56.2 args: --verbose test: needs: lint diff --git a/go.mod b/go.mod index 0b60c5d73b..fbbce7c0fc 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,16 @@ module github.com/gin-gonic/gin go 1.20 require ( - github.com/bytedance/sonic v1.10.2 + github.com/bytedance/sonic v1.11.0 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.17.0 + github.com/go-playground/validator/v10 v10.18.0 github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.20 github.com/pelletier/go-toml/v2 v2.1.1 github.com/stretchr/testify v1.8.4 github.com/ugorji/go/codec v1.2.12 - golang.org/x/net v0.20.0 + golang.org/x/net v0.21.0 google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -25,13 +25,13 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect - github.com/leodido/go-urn v1.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.7.0 // indirect - golang.org/x/crypto v0.18.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index e360d9d28b..ce6c7fe703 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= -github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.0 h1:FwNNv6Vu4z2Onf1++LNzxB/QhitD8wuTdpZzMTGITWo= +github.com/bytedance/sonic v1.11.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= @@ -21,8 +21,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= -github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= @@ -33,8 +33,8 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/leodido/go-urn v1.3.0 h1:jX8FDLfW4ThVXctBNZ+3cIWnCSnrACDV73r76dy0aQQ= -github.com/leodido/go-urn v1.3.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -63,14 +63,14 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= From 739d2d9c80e0298dafb5df1c30bae35d63935d6c Mon Sep 17 00:00:00 2001 From: Name <1911860538@qq.com> Date: Tue, 5 Mar 2024 14:07:11 +0800 Subject: [PATCH 267/346] chore(perf): Optimize the Copy method of the Context struct (#3859) * Optimize the Copy method of the Context struct: using 'make' to initialize the map('cp.Keys') with a length of 'c.Keys'; avoiding repeatedly assiging the 'params' to 'context'. * Using temporary variables to save c.Keys and c.Params to prevent them from changing during the copying process. --------- Co-authored-by: huangzw --- context.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/context.go b/context.go index 420ff1678f..126d35dba7 100644 --- a/context.go +++ b/context.go @@ -113,20 +113,24 @@ func (c *Context) Copy() *Context { cp := Context{ writermem: c.writermem, Request: c.Request, - Params: c.Params, engine: c.engine, } + cp.writermem.ResponseWriter = nil cp.Writer = &cp.writermem cp.index = abortIndex cp.handlers = nil - cp.Keys = map[string]any{} - for k, v := range c.Keys { + + cKeys := c.Keys + cp.Keys = make(map[string]any, len(cKeys)) + for k, v := range cKeys { cp.Keys[k] = v } - paramCopy := make([]Param, len(cp.Params)) - copy(paramCopy, cp.Params) - cp.Params = paramCopy + + cParams := c.Params + cp.Params = make([]Param, len(cParams)) + copy(cp.Params, cParams) + return &cp } From ae15646aba14cd8245fbebd263cc7740c6789ef3 Mon Sep 17 00:00:00 2001 From: guangwu Date: Tue, 5 Mar 2024 14:36:02 +0800 Subject: [PATCH 268/346] test(http): use constant instead of numeric literal (#3863) Signed-off-by: guoguangwu --- routes_test.go | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/routes_test.go b/routes_test.go index a0ff695fd9..185abd9920 100644 --- a/routes_test.go +++ b/routes_test.go @@ -180,58 +180,58 @@ func TestRouteRedirectTrailingSlash(t *testing.T) { w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/api"}) assert.Equal(t, "/api/path2/", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"}) - assert.Equal(t, 200, w.Code) + assert.Equal(t, http.StatusOK, w.Code) w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../api#?"}) assert.Equal(t, "/api/path", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../api"}) assert.Equal(t, "/api/path", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "../../api"}) assert.Equal(t, "/api/path2/", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/../../api"}) assert.Equal(t, "/api/path2/", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "api/../../"}) assert.Equal(t, "//path", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "api/../../../"}) assert.Equal(t, "/path", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "../../gin-gonic.com"}) assert.Equal(t, "/gin-goniccom/path2/", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/../../gin-gonic.com"}) assert.Equal(t, "/gin-goniccom/path2/", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "https://gin-gonic.com/#"}) assert.Equal(t, "https/gin-goniccom/https/gin-goniccom/path", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "#api"}) assert.Equal(t, "api/api/path", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "/nor-mal/#?a=1"}) assert.Equal(t, "/nor-mal/a1/path", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "/nor-mal/%2e%2e/"}) assert.Equal(t, "/nor-mal/2e2e/path", w.Header().Get("Location")) - assert.Equal(t, 301, w.Code) + assert.Equal(t, http.StatusMovedPermanently, w.Code) router.RedirectTrailingSlash = false @@ -619,11 +619,11 @@ func TestRouterNotFound(t *testing.T) { router = New() router.NoRoute(func(c *Context) { if c.Request.RequestURI == "/login" { - c.String(200, "login") + c.String(http.StatusOK, "login") } }) router.GET("/logout", func(c *Context) { - c.String(200, "logout") + c.String(http.StatusOK, "logout") }) w = PerformRequest(router, http.MethodGet, "/login") assert.Equal(t, "login", w.Body.String()) @@ -635,7 +635,7 @@ func TestRouterStaticFSNotFound(t *testing.T) { router := New() router.StaticFS("/", http.FileSystem(http.Dir("/thisreallydoesntexist/"))) router.NoRoute(func(c *Context) { - c.String(404, "non existent") + c.String(http.StatusNotFound, "non existent") }) w := PerformRequest(router, http.MethodGet, "/nonexistent") @@ -718,12 +718,12 @@ func TestRouteRawPathNoUnescape(t *testing.T) { func TestRouteServeErrorWithWriteHeader(t *testing.T) { route := New() route.Use(func(c *Context) { - c.Status(421) + c.Status(http.StatusMisdirectedRequest) c.Next() }) w := PerformRequest(route, http.MethodGet, "/NotFound") - assert.Equal(t, 421, w.Code) + assert.Equal(t, http.StatusMisdirectedRequest, w.Code) assert.Equal(t, 0, w.Body.Len()) } From 9c61295efeea99f6c9d1722294f1bf61d8e464d6 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 5 Mar 2024 14:54:35 +0100 Subject: [PATCH 269/346] chore(header): Add support for RFC 9512: application/yaml (#3851) * fix(binding): support application/yaml RFC 9512 defines application/yaml as the official YAML MIME type. application/x-yaml is deprecated. In this commit, we ensure it is recognized correctly in Content-Type. * fix(render): use application/yaml when rendering YAML As per RFC 9512, application/x-yaml is now deprecated and applications should use application/yaml. This commit fix the Content-Type header when rendering YAML. --- binding/binding.go | 3 ++- binding/binding_nomsgpack.go | 3 ++- binding/binding_test.go | 2 ++ context_test.go | 6 +++--- render/render_test.go | 4 ++-- render/yaml.go | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/binding/binding.go b/binding/binding.go index 40948529a3..036b329b1c 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -21,6 +21,7 @@ const ( MIMEMSGPACK = "application/x-msgpack" MIMEMSGPACK2 = "application/msgpack" MIMEYAML = "application/x-yaml" + MIMEYAML2 = "application/yaml" MIMETOML = "application/toml" ) @@ -102,7 +103,7 @@ func Default(method, contentType string) Binding { return ProtoBuf case MIMEMSGPACK, MIMEMSGPACK2: return MsgPack - case MIMEYAML: + case MIMEYAML, MIMEYAML2: return YAML case MIMETOML: return TOML diff --git a/binding/binding_nomsgpack.go b/binding/binding_nomsgpack.go index 93ad8ba304..552a86b2d7 100644 --- a/binding/binding_nomsgpack.go +++ b/binding/binding_nomsgpack.go @@ -19,6 +19,7 @@ const ( MIMEMultipartPOSTForm = "multipart/form-data" MIMEPROTOBUF = "application/x-protobuf" MIMEYAML = "application/x-yaml" + MIMEYAML2 = "application/yaml" MIMETOML = "application/toml" ) @@ -96,7 +97,7 @@ func Default(method, contentType string) Binding { return XML case MIMEPROTOBUF: return ProtoBuf - case MIMEYAML: + case MIMEYAML, MIMEYAML2: return YAML case MIMEMultipartPOSTForm: return FormMultipart diff --git a/binding/binding_test.go b/binding/binding_test.go index 9af4f88aa3..feb8eed558 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -164,6 +164,8 @@ func TestBindingDefault(t *testing.T) { assert.Equal(t, YAML, Default("POST", MIMEYAML)) assert.Equal(t, YAML, Default("PUT", MIMEYAML)) + assert.Equal(t, YAML, Default("POST", MIMEYAML2)) + assert.Equal(t, YAML, Default("PUT", MIMEYAML2)) assert.Equal(t, TOML, Default("POST", MIMETOML)) assert.Equal(t, TOML, Default("PUT", MIMETOML)) diff --git a/context_test.go b/context_test.go index 88165c07c9..d060ccf03f 100644 --- a/context_test.go +++ b/context_test.go @@ -1060,7 +1060,7 @@ func TestContextRenderUTF8Attachment(t *testing.T) { } // TestContextRenderYAML tests that the response is serialized as YAML -// and Content-Type is set to application/x-yaml +// and Content-Type is set to application/yaml func TestContextRenderYAML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) @@ -1069,7 +1069,7 @@ func TestContextRenderYAML(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "foo: bar\n", w.Body.String()) - assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type")) + assert.Equal(t, "application/yaml; charset=utf-8", w.Header().Get("Content-Type")) } // TestContextRenderTOML tests that the response is serialized as TOML @@ -1217,7 +1217,7 @@ func TestContextNegotiationWithYAML(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "foo: bar\n", w.Body.String()) - assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type")) + assert.Equal(t, "application/yaml; charset=utf-8", w.Header().Get("Content-Type")) } func TestContextNegotiationWithTOML(t *testing.T) { diff --git a/render/render_test.go b/render/render_test.go index c9db635f04..145f131641 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -280,12 +280,12 @@ b: d: [3, 4] ` (YAML{data}).WriteContentType(w) - assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type")) + assert.Equal(t, "application/yaml; charset=utf-8", w.Header().Get("Content-Type")) err := (YAML{data}).Render(w) assert.NoError(t, err) assert.Equal(t, "|4-\n a : Easy!\n b:\n \tc: 2\n \td: [3, 4]\n \t\n", w.Body.String()) - assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type")) + assert.Equal(t, "application/yaml; charset=utf-8", w.Header().Get("Content-Type")) } type fail struct{} diff --git a/render/yaml.go b/render/yaml.go index fc927c1f28..042bb821da 100644 --- a/render/yaml.go +++ b/render/yaml.go @@ -15,7 +15,7 @@ type YAML struct { Data any } -var yamlContentType = []string{"application/x-yaml; charset=utf-8"} +var yamlContentType = []string{"application/yaml; charset=utf-8"} // Render (YAML) marshals the given interface object and writes data with custom ContentType. func (r YAML) Render(w http.ResponseWriter) error { From f75144a356e57c95bd21a048f0a40492dcdb33c5 Mon Sep 17 00:00:00 2001 From: guangwu Date: Tue, 5 Mar 2024 21:55:25 +0800 Subject: [PATCH 270/346] docs: fix typo in comment (#3868) Signed-off-by: guoguangwu --- routes_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes_test.go b/routes_test.go index 185abd9920..73f393e71c 100644 --- a/routes_test.go +++ b/routes_test.go @@ -339,7 +339,7 @@ func TestRouteParamsByNameWithExtraSlash(t *testing.T) { // TestRouteParamsNotEmpty tests that context parameters will be set // even if a route with params/wildcards is registered after the context -// initialisation (which happened in a previous requets). +// initialisation (which happened in a previous requests). func TestRouteParamsNotEmpty(t *testing.T) { name := "" lastName := "" From 09f8224593e31edf3c58ab3f13bc31ef53473733 Mon Sep 17 00:00:00 2001 From: Karthik Reddy Puli <47525322+KarthikReddyPuli@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:16:53 +0530 Subject: [PATCH 271/346] fix(route): Add fullPath in context copy (#3784) * fix: Add fullPath in context copy * Update context.go --------- Co-authored-by: Bo-Yi Wu --- context.go | 1 + context_test.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/context.go b/context.go index 126d35dba7..609827dd67 100644 --- a/context.go +++ b/context.go @@ -120,6 +120,7 @@ func (c *Context) Copy() *Context { cp.Writer = &cp.writermem cp.index = abortIndex cp.handlers = nil + cp.fullPath = c.fullPath cKeys := c.Keys cp.Keys = make(map[string]any, len(cKeys)) diff --git a/context_test.go b/context_test.go index d060ccf03f..ac766e2b8f 100644 --- a/context_test.go +++ b/context_test.go @@ -324,6 +324,7 @@ func TestContextCopy(t *testing.T) { c.handlers = HandlersChain{func(c *Context) {}} c.Params = Params{Param{Key: "foo", Value: "bar"}} c.Set("foo", "bar") + c.fullPath = "/hola" cp := c.Copy() assert.Nil(t, cp.handlers) @@ -336,6 +337,7 @@ func TestContextCopy(t *testing.T) { assert.Equal(t, cp.Params, c.Params) cp.Set("foo", "notBar") assert.False(t, cp.Keys["foo"] == c.Keys["foo"]) + assert.Equal(t, cp.fullPath, c.fullPath) } func TestContextHandlerName(t *testing.T) { From 3ea8bd99fbb4e499d70a0c8e1ce2ce4b7c6348b6 Mon Sep 17 00:00:00 2001 From: jessetang <1430482733@qq.com> Date: Wed, 6 Mar 2024 22:27:21 +0800 Subject: [PATCH 272/346] chore(refactor): modify interface check way (#3855) Signed-off-by: demoManito <1430482733@qq.com> --- render/render.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/render/render.go b/render/render.go index 7955000c6e..4bdcfa2326 100644 --- a/render/render.go +++ b/render/render.go @@ -15,22 +15,22 @@ type Render interface { } var ( - _ Render = JSON{} - _ Render = IndentedJSON{} - _ Render = SecureJSON{} - _ Render = JsonpJSON{} - _ Render = XML{} - _ Render = String{} - _ Render = Redirect{} - _ Render = Data{} - _ Render = HTML{} - _ HTMLRender = HTMLDebug{} - _ HTMLRender = HTMLProduction{} - _ Render = YAML{} - _ Render = Reader{} - _ Render = AsciiJSON{} - _ Render = ProtoBuf{} - _ Render = TOML{} + _ Render = (*JSON)(nil) + _ Render = (*IndentedJSON)(nil) + _ Render = (*SecureJSON)(nil) + _ Render = (*JsonpJSON)(nil) + _ Render = (*XML)(nil) + _ Render = (*String)(nil) + _ Render = (*Redirect)(nil) + _ Render = (*Data)(nil) + _ Render = (*HTML)(nil) + _ HTMLRender = (*HTMLDebug)(nil) + _ HTMLRender = (*HTMLProduction)(nil) + _ Render = (*YAML)(nil) + _ Render = (*Reader)(nil) + _ Render = (*AsciiJSON)(nil) + _ Render = (*ProtoBuf)(nil) + _ Render = (*TOML)(nil) ) func writeContentType(w http.ResponseWriter, value []string) { From 97eab7d09a8b048cab4a3d8ebd6c0ea78284c716 Mon Sep 17 00:00:00 2001 From: jessetang <1430482733@qq.com> Date: Fri, 8 Mar 2024 15:56:00 +0800 Subject: [PATCH 273/346] test(git): gitignore add develop tools (#3370) --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index bdd50c95cf..1ea0e2b950 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ count.out test profile.out tmp.out + +# Develop tools +.idea/ +.vscode/ From 5f458dd1a6d631f324e4af9a4f5429ffdf199342 Mon Sep 17 00:00:00 2001 From: Endless Paradox <129645532+EndlessParadox1@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:22:58 +0800 Subject: [PATCH 274/346] feat(auth): add proxy-server authentication (#3877) --- auth.go | 21 +++++++++++++++++++++ auth_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/auth.go b/auth.go index 2503c5156a..cc6c5a7e41 100644 --- a/auth.go +++ b/auth.go @@ -15,6 +15,7 @@ import ( // AuthUserKey is the cookie name for user credential in basic auth. const AuthUserKey = "user" +const AuthProxyUserKey = "proxy_user" // Accounts defines a key/value for user/pass list of authorized logins. type Accounts map[string]string @@ -89,3 +90,23 @@ func authorizationHeader(user, password string) string { base := user + ":" + password return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base)) } + +func BasicAuthForProxy(accounts Accounts, realm string) HandlerFunc { + if realm == "" { + realm = "Proxy Authorization Required" + } + realm = "Basic realm=" + strconv.Quote(realm) + pairs := processAccounts(accounts) + return func(c *Context) { + proxyUser, found := pairs.searchCredential(c.requestHeader("Proxy-Authorization")) + if !found { + // Credentials doesn't match, we return 407 and abort handlers chain. + c.Header("Proxy-Authenticate", realm) + c.AbortWithStatus(http.StatusProxyAuthRequired) + return + } + // The proxy_user credentials was found, set proxy_user's id to key AuthProxyUserKey in this context, the proxy_user's id can be read later using + // c.MustGet(gin.AuthProxyUserKey). + c.Set(AuthProxyUserKey, proxyUser) + } +} diff --git a/auth_test.go b/auth_test.go index 42b6f8fddf..f7175929b4 100644 --- a/auth_test.go +++ b/auth_test.go @@ -137,3 +137,40 @@ func TestBasicAuth401WithCustomRealm(t *testing.T) { assert.Equal(t, http.StatusUnauthorized, w.Code) assert.Equal(t, "Basic realm=\"My Custom \\\"Realm\\\"\"", w.Header().Get("WWW-Authenticate")) } + +func TestBasicAuthForProxySucceed(t *testing.T) { + accounts := Accounts{"admin": "password"} + router := New() + router.Use(BasicAuthForProxy(accounts, "")) + router.Any("/*proxyPath", func(c *Context) { + c.String(http.StatusOK, c.MustGet(AuthProxyUserKey).(string)) + }) + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/test", nil) + req.Header.Set("Proxy-Authorization", authorizationHeader("admin", "password")) + router.ServeHTTP(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "admin", w.Body.String()) +} + +func TestBasicAuthForProxy407(t *testing.T) { + called := false + accounts := Accounts{"foo": "bar"} + router := New() + router.Use(BasicAuthForProxy(accounts, "")) + router.Any("/*proxyPath", func(c *Context) { + called = true + c.String(http.StatusOK, c.MustGet(AuthProxyUserKey).(string)) + }) + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/test", nil) + req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password"))) + router.ServeHTTP(w, req) + + assert.False(t, called) + assert.Equal(t, http.StatusProxyAuthRequired, w.Code) + assert.Equal(t, "Basic realm=\"Proxy Authorization Required\"", w.Header().Get("Proxy-Authenticate")) +} From 646312aef6a34095476ac846b0920db5fb24b2ea Mon Sep 17 00:00:00 2001 From: qingmu Date: Mon, 11 Mar 2024 22:24:36 +0800 Subject: [PATCH 275/346] fix: protect Context.Keys map when call Copy method (#3873) --- context.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/context.go b/context.go index 609827dd67..59abee1715 100644 --- a/context.go +++ b/context.go @@ -124,9 +124,11 @@ func (c *Context) Copy() *Context { cKeys := c.Keys cp.Keys = make(map[string]any, len(cKeys)) + c.mu.RLock() for k, v := range cKeys { cp.Keys[k] = v } + c.mu.RUnlock() cParams := c.Params cp.Params = make([]Param, len(cParams)) From 83fc7673f9797b4c7d8d1c41b94e9922303e6275 Mon Sep 17 00:00:00 2001 From: TotomiEcio <63461656+TotomiEcio@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:25:28 -0300 Subject: [PATCH 276/346] docs: fix typo in function documentation (#3872) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 59abee1715..a17a58e9f4 100644 --- a/context.go +++ b/context.go @@ -393,7 +393,7 @@ func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) // // router.GET("/user/:id", func(c *gin.Context) { // // a GET request to /user/john -// id := c.Param("id") // id == "/john" +// id := c.Param("id") // id == "john" // // a GET request to /user/john/ // id := c.Param("id") // id == "/john/" // }) From ac5e84d93ce34359bfd2f346cb2971ea754d83e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Flc=E3=82=9B?= Date: Mon, 11 Mar 2024 22:35:30 +0800 Subject: [PATCH 277/346] feat(engine): Added `OptionFunc` and `With` (#3572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Added `OptionFunc` and `With` * fix: `With(opts...)` must be after `New` * feat: improve New with * fix: test * optimize code * optimize nolint * optimize code Signed-off-by: Flc゛ --------- Signed-off-by: Flc゛ --- context_test.go | 8 ++++---- gin.go | 20 ++++++++++++++++---- gin_test.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/context_test.go b/context_test.go index ac766e2b8f..33cc43f971 100644 --- a/context_test.go +++ b/context_test.go @@ -1000,7 +1000,7 @@ func TestContextRenderFile(t *testing.T) { c.File("./gin.go") assert.Equal(t, http.StatusOK, w.Code) - assert.Contains(t, w.Body.String(), "func New() *Engine {") + assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {") // Content-Type='text/plain; charset=utf-8' when go version <= 1.16, // else, Content-Type='text/x-go; charset=utf-8' assert.NotEqual(t, "", w.Header().Get("Content-Type")) @@ -1014,7 +1014,7 @@ func TestContextRenderFileFromFS(t *testing.T) { c.FileFromFS("./gin.go", Dir(".", false)) assert.Equal(t, http.StatusOK, w.Code) - assert.Contains(t, w.Body.String(), "func New() *Engine {") + assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {") // Content-Type='text/plain; charset=utf-8' when go version <= 1.16, // else, Content-Type='text/x-go; charset=utf-8' assert.NotEqual(t, "", w.Header().Get("Content-Type")) @@ -1030,7 +1030,7 @@ func TestContextRenderAttachment(t *testing.T) { c.FileAttachment("./gin.go", newFilename) assert.Equal(t, 200, w.Code) - assert.Contains(t, w.Body.String(), "func New() *Engine {") + assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {") assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition")) } @@ -1057,7 +1057,7 @@ func TestContextRenderUTF8Attachment(t *testing.T) { c.FileAttachment("./gin.go", newFilename) assert.Equal(t, 200, w.Code) - assert.Contains(t, w.Body.String(), "func New() *Engine {") + assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {") assert.Equal(t, `attachment; filename*=UTF-8''`+url.QueryEscape(newFilename), w.Header().Get("Content-Disposition")) } diff --git a/gin.go b/gin.go index 24a9864aff..1633fe13f9 100644 --- a/gin.go +++ b/gin.go @@ -47,6 +47,9 @@ var regRemoveRepeatedChar = regexp.MustCompile("/{2,}") // HandlerFunc defines the handler used by gin middleware as return value. type HandlerFunc func(*Context) +// OptionFunc defines the function to change the default configuration +type OptionFunc func(*Engine) + // HandlersChain defines a HandlerFunc slice. type HandlersChain []HandlerFunc @@ -182,7 +185,7 @@ var _ IRouter = (*Engine)(nil) // - ForwardedByClientIP: true // - UseRawPath: false // - UnescapePathValues: true -func New() *Engine { +func New(opts ...OptionFunc) *Engine { debugPrintWARNINGNew() engine := &Engine{ RouterGroup: RouterGroup{ @@ -211,15 +214,15 @@ func New() *Engine { engine.pool.New = func() any { return engine.allocateContext(engine.maxParams) } - return engine + return engine.With(opts...) } // Default returns an Engine instance with the Logger and Recovery middleware already attached. -func Default() *Engine { +func Default(opts ...OptionFunc) *Engine { debugPrintWARNINGDefault() engine := New() engine.Use(Logger(), Recovery()) - return engine + return engine.With(opts...) } func (engine *Engine) Handler() http.Handler { @@ -313,6 +316,15 @@ func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { return engine } +// With returns a new Engine instance with the provided options. +func (engine *Engine) With(opts ...OptionFunc) *Engine { + for _, opt := range opts { + opt(engine) + } + + return engine +} + func (engine *Engine) rebuild404Handlers() { engine.allNoRoute = engine.combineHandlers(engine.noRoute) } diff --git a/gin_test.go b/gin_test.go index 8825ac7ef8..4550a7e5c8 100644 --- a/gin_test.go +++ b/gin_test.go @@ -696,3 +696,37 @@ func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) func handlerTest1(c *Context) {} func handlerTest2(c *Context) {} + +func TestNewOptionFunc(t *testing.T) { + var fc = func(e *Engine) { + e.GET("/test1", handlerTest1) + e.GET("/test2", handlerTest2) + + e.Use(func(c *Context) { + c.Next() + }) + } + + r := New(fc) + + routes := r.Routes() + assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"}) + assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"}) +} + +func TestWithOptionFunc(t *testing.T) { + r := New() + + r.With(func(e *Engine) { + e.GET("/test1", handlerTest1) + e.GET("/test2", handlerTest2) + + e.Use(func(c *Context) { + c.Next() + }) + }) + + routes := r.Routes() + assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"}) + assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"}) +} From 1b3c0859693fc85290c01ba098b1440d4776549f Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 11 Mar 2024 10:41:07 -0400 Subject: [PATCH 278/346] chore(debug): add ability to override the debugPrint statement (#2337) * feat: add ability to override the debugPrint statement This allows users to use a single logger within their application for all printing, regardless of level. * chore: make the code more readable, as per review comment * fix: use tab instead of space for indentation --- debug.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/debug.go b/debug.go index 1fc0cafe10..1761fe325e 100644 --- a/debug.go +++ b/debug.go @@ -23,6 +23,9 @@ func IsDebugging() bool { // DebugPrintRouteFunc indicates debug log output format. var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int) +// DebugPrintFunc indicates debug log output format. +var DebugPrintFunc func(format string, values ...interface{}) + func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) { if IsDebugging() { nuHandlers := len(handlers) @@ -48,12 +51,19 @@ func debugPrintLoadTemplate(tmpl *template.Template) { } func debugPrint(format string, values ...any) { - if IsDebugging() { - if !strings.HasSuffix(format, "\n") { - format += "\n" - } - fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...) + if !IsDebugging() { + return + } + + if DebugPrintFunc != nil { + DebugPrintFunc(format, values...) + return + } + + if !strings.HasSuffix(format, "\n") { + format += "\n" } + fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...) } func getMinVer(v string) (uint64, error) { From ab8042e9e5370bbe0e93ea5adc6e74ae4c5df95e Mon Sep 17 00:00:00 2001 From: Noah Yao Date: Mon, 11 Mar 2024 22:44:28 +0800 Subject: [PATCH 279/346] chore(request): check reader if it's nil before reading (#3419) --- context.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/context.go b/context.go index a17a58e9f4..0c73a49f9d 100644 --- a/context.go +++ b/context.go @@ -880,6 +880,9 @@ func (c *Context) GetHeader(key string) string { // GetRawData returns stream data. func (c *Context) GetRawData() ([]byte, error) { + if c.Request.Body == nil { + return nil, errors.New("cannot read nil body") + } return io.ReadAll(c.Request.Body) } From f70dd00b00bc0a46cb18b55bfe1f918d5d29b511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Flc=E3=82=9B?= Date: Tue, 12 Mar 2024 13:49:23 +0800 Subject: [PATCH 280/346] fix(engine): fix unit test (#3878) * fix(engine): fix unit test * fix(engine): fix unit test --- context_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context_test.go b/context_test.go index 33cc43f971..089047c29d 100644 --- a/context_test.go +++ b/context_test.go @@ -1044,7 +1044,7 @@ func TestContextRenderAndEscapeAttachment(t *testing.T) { c.FileAttachment("./gin.go", maliciousFilename) assert.Equal(t, 200, w.Code) - assert.Contains(t, w.Body.String(), "func New() *Engine {") + assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {") assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", actualEscapedResponseFilename), w.Header().Get("Content-Disposition")) } From 861ffb9181dc811dc5d76fc450b36d3e68850b95 Mon Sep 17 00:00:00 2001 From: Endless Paradox <129645532+EndlessParadox1@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:51:04 +0800 Subject: [PATCH 281/346] docs(middleware): comments to function `BasicAuthForProxy` (#3881) --- auth.go | 1 + 1 file changed, 1 insertion(+) diff --git a/auth.go b/auth.go index cc6c5a7e41..2ed33ac0d2 100644 --- a/auth.go +++ b/auth.go @@ -91,6 +91,7 @@ func authorizationHeader(user, password string) string { return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base)) } +// BasicAuthForProxy returns a Basic HTTP Proxy-Authorization middleware. func BasicAuthForProxy(accounts Accounts, realm string) HandlerFunc { if realm == "" { realm = "Proxy Authorization Required" From 990c44aebf20f0796d99051e53d6ee75b7ed52fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Flc=E3=82=9B?= Date: Tue, 12 Mar 2024 13:55:52 +0800 Subject: [PATCH 282/346] docs(context): Added deprecation comments to BindWith (#3880) --- deprecated.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deprecated.go b/deprecated.go index 9521308f1d..b4c6cd88e8 100644 --- a/deprecated.go +++ b/deprecated.go @@ -12,6 +12,8 @@ import ( // BindWith binds the passed struct pointer using the specified binding engine. // See the binding package. +// +// Deprecated: Use MustBindWith or ShouldBindWith. func (c *Context) BindWith(obj any, b binding.Binding) error { log.Println(`BindWith(\"any, binding.Binding\") error is going to be deprecated, please check issue #662 and either use MustBindWith() if you From ee70b30a97205ac1f32889f41d8a494b3b2c81a5 Mon Sep 17 00:00:00 2001 From: Endless Paradox <129645532+EndlessParadox1@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:22:05 +0800 Subject: [PATCH 283/346] docs: Add document to constant `AuthProxyUserKey` and `BasicAuthForProxy`. (#3887) --- auth.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/auth.go b/auth.go index 2ed33ac0d2..5d3222d566 100644 --- a/auth.go +++ b/auth.go @@ -15,6 +15,8 @@ import ( // AuthUserKey is the cookie name for user credential in basic auth. const AuthUserKey = "user" + +// AuthProxyUserKey is the cookie name for proxy_user credential in basic auth for proxy. const AuthProxyUserKey = "proxy_user" // Accounts defines a key/value for user/pass list of authorized logins. @@ -92,6 +94,7 @@ func authorizationHeader(user, password string) string { } // BasicAuthForProxy returns a Basic HTTP Proxy-Authorization middleware. +// If the realm is empty, "Proxy Authorization Required" will be used by default. func BasicAuthForProxy(accounts Accounts, realm string) HandlerFunc { if realm == "" { realm = "Proxy Authorization Required" From fd60a24ab76c3c92955ba253c1f7eda9e4981c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Flc=E3=82=9B?= Date: Thu, 14 Mar 2024 11:22:54 +0800 Subject: [PATCH 284/346] test(path): Optimize unit test execution results (#3883) * test(path): Add a GC recycle validation * test(path): Optimize unit test execution results * test(path): Optimize unit test execution results --- path_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/path_test.go b/path_test.go index caefd63a80..864302f4e1 100644 --- a/path_test.go +++ b/path_test.go @@ -6,6 +6,7 @@ package gin import ( + "runtime" "strings" "testing" @@ -80,6 +81,10 @@ func TestPathCleanMallocs(t *testing.T) { t.Skip("skipping malloc count in short mode") } + if runtime.GOMAXPROCS(0) > 1 { + t.Skip("skipping malloc count; GOMAXPROCS>1") + } + for _, test := range cleanTests { allocs := testing.AllocsPerRun(100, func() { cleanPath(test.result) }) assert.EqualValues(t, allocs, 0) From 0d9dbbb44551a872d30fd89d4d55ba0515d646fd Mon Sep 17 00:00:00 2001 From: Guilherme Aleixo Date: Mon, 18 Mar 2024 11:14:06 -0300 Subject: [PATCH 285/346] chore(security): upgrade Protobuf for CVE-2024-24786 (#3893) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fbbce7c0fc..11ce23e80e 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/stretchr/testify v1.8.4 github.com/ugorji/go/codec v1.2.12 golang.org/x/net v0.21.0 - google.golang.org/protobuf v1.32.0 + google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index ce6c7fe703..49eae34304 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 2b1da2b0b38dfc5d5841266037c0c8b249eca1dd Mon Sep 17 00:00:00 2001 From: "Farmer.Chillax" <48387781+FarmerChillax@users.noreply.github.com> Date: Thu, 21 Mar 2024 21:08:41 +0800 Subject: [PATCH 286/346] fix(context): make context Value method adhere to Go standards (#3897) --- context.go | 6 +++++- context_test.go | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 0c73a49f9d..3a9608d5fb 100644 --- a/context.go +++ b/context.go @@ -43,6 +43,10 @@ const BodyBytesKey = "_gin-gonic/gin/bodybyteskey" // ContextKey is the key that a Context returns itself for. const ContextKey = "_gin-gonic/gin/contextkey" +type ContextKeyType int + +const ContextRequestKey ContextKeyType = 0 + // abortIndex represents a typical value used in abort functions. const abortIndex int8 = math.MaxInt8 >> 1 @@ -1225,7 +1229,7 @@ func (c *Context) Err() error { // if no value is associated with key. Successive calls to Value with // the same key returns the same result. func (c *Context) Value(key any) any { - if key == 0 { + if key == ContextRequestKey { return c.Request } if key == ContextKey { diff --git a/context_test.go b/context_test.go index 089047c29d..9c1717ed20 100644 --- a/context_test.go +++ b/context_test.go @@ -1985,7 +1985,7 @@ func TestContextGolangContext(t *testing.T) { ti, ok := c.Deadline() assert.Equal(t, ti, time.Time{}) assert.False(t, ok) - assert.Equal(t, c.Value(0), c.Request) + assert.Equal(t, c.Value(ContextRequestKey), c.Request) assert.Equal(t, c.Value(ContextKey), c) assert.Nil(t, c.Value("foo")) From 78f4687875d72d10392f8a77008cbefdec4c0aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Flc=E3=82=9B?= Date: Thu, 21 Mar 2024 21:13:56 +0800 Subject: [PATCH 287/346] build(codecov): Added a codecov configuration (#3891) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Flc゛ --- codecov.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 codecov.yml diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..47782e50d6 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,13 @@ +coverage: + require_ci_to_pass: true + + status: + project: + default: + target: 99% + threshold: 99% + + patch: + default: + target: 99% + threshold: 95% \ No newline at end of file From 8790d08909fc4d193c6c787c9c72f3089168f411 Mon Sep 17 00:00:00 2001 From: illiafox <61962654+illiafox@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:28:42 +0200 Subject: [PATCH 288/346] fix(uri): query binding bug (#3236) * fix query mapping * query binding test --- binding/query.go | 4 ++-- binding/query_test.go | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 binding/query_test.go diff --git a/binding/query.go b/binding/query.go index c958b88bda..baa4aead4f 100644 --- a/binding/query.go +++ b/binding/query.go @@ -12,9 +12,9 @@ func (queryBinding) Name() string { return "query" } -func (queryBinding) Bind(req *http.Request, obj any) error { +func (q queryBinding) Bind(req *http.Request, obj any) error { values := req.URL.Query() - if err := mapForm(obj, values); err != nil { + if err := mapFormByTag(obj, values, q.Name()); err != nil { return err } return validate(obj) diff --git a/binding/query_test.go b/binding/query_test.go new file mode 100644 index 0000000000..72102040ea --- /dev/null +++ b/binding/query_test.go @@ -0,0 +1,23 @@ +package binding + +import ( + "net/http" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestQueryBinding(t *testing.T) { + var s struct { + Foo string `query:"foo"` + } + + request := &http.Request{URL: &url.URL{RawQuery: "foo=BAR"}} + + err := queryBinding{}.Bind(request, &s) + require.NoError(t, err) + + assert.Equal(t, "BAR", s.Foo) +} From d4e413648824333726ef65de5defc457e9dbf095 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 22 Mar 2024 10:01:27 +0800 Subject: [PATCH 289/346] Revert "fix(uri): query binding bug (#3236)" (#3899) This reverts commit 8790d08909fc4d193c6c787c9c72f3089168f411. --- binding/query.go | 4 ++-- binding/query_test.go | 23 ----------------------- 2 files changed, 2 insertions(+), 25 deletions(-) delete mode 100644 binding/query_test.go diff --git a/binding/query.go b/binding/query.go index baa4aead4f..c958b88bda 100644 --- a/binding/query.go +++ b/binding/query.go @@ -12,9 +12,9 @@ func (queryBinding) Name() string { return "query" } -func (q queryBinding) Bind(req *http.Request, obj any) error { +func (queryBinding) Bind(req *http.Request, obj any) error { values := req.URL.Query() - if err := mapFormByTag(obj, values, q.Name()); err != nil { + if err := mapForm(obj, values); err != nil { return err } return validate(obj) diff --git a/binding/query_test.go b/binding/query_test.go deleted file mode 100644 index 72102040ea..0000000000 --- a/binding/query_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package binding - -import ( - "net/http" - "net/url" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestQueryBinding(t *testing.T) { - var s struct { - Foo string `query:"foo"` - } - - request := &http.Request{URL: &url.URL{RawQuery: "foo=BAR"}} - - err := queryBinding{}.Bind(request, &s) - require.NoError(t, err) - - assert.Equal(t, "BAR", s.Foo) -} From fd1faaded01aef14a3955ec076f1cbeb9cb87775 Mon Sep 17 00:00:00 2001 From: ssfyn Date: Sat, 23 Mar 2024 08:50:25 +0800 Subject: [PATCH 290/346] feat(binding): support override default binding implement (#3514) --- binding/binding.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/binding/binding.go b/binding/binding.go index 036b329b1c..94723879a7 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -73,18 +73,18 @@ var Validator StructValidator = &defaultValidator{} // These implement the Binding interface and can be used to bind the data // present in the request to struct instances. var ( - JSON = jsonBinding{} - XML = xmlBinding{} - Form = formBinding{} - Query = queryBinding{} - FormPost = formPostBinding{} - FormMultipart = formMultipartBinding{} - ProtoBuf = protobufBinding{} - MsgPack = msgpackBinding{} - YAML = yamlBinding{} - Uri = uriBinding{} - Header = headerBinding{} - TOML = tomlBinding{} + JSON BindingBody = jsonBinding{} + XML BindingBody = xmlBinding{} + Form Binding = formBinding{} + Query Binding = queryBinding{} + FormPost Binding = formPostBinding{} + FormMultipart Binding = formMultipartBinding{} + ProtoBuf BindingBody = protobufBinding{} + MsgPack BindingBody = msgpackBinding{} + YAML BindingBody = yamlBinding{} + Uri BindingUri = uriBinding{} + Header Binding = headerBinding{} + TOML BindingBody = tomlBinding{} ) // Default returns the appropriate Binding instance based on the HTTP method From 7a865dcf1dbe6ec52e074b1ddce830d278eb72cf Mon Sep 17 00:00:00 2001 From: RedCrazyGhost <49381700+RedCrazyGhost@users.noreply.github.com> Date: Sat, 23 Mar 2024 22:09:02 +0800 Subject: [PATCH 291/346] feat(bind): ShouldBindBodyWith shortcut and change doc (#3871) * feat: ShouldBindBodyWith shortcut and change doc * fix: yaml can parse json test case * style: fix new test case in context_test.go * chore: modify the code style to specify binding type * chroe: gofmt modifies the code format --- context.go | 20 ++++ context_test.go | 257 ++++++++++++++++++++++++++++++++++++++++++++++++ docs/doc.md | 9 +- 3 files changed, 284 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 3a9608d5fb..afc3c353e1 100644 --- a/context.go +++ b/context.go @@ -774,6 +774,26 @@ func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error return bb.BindBody(body, obj) } +// ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON). +func (c *Context) ShouldBindBodyWithJSON(obj any) error { + return c.ShouldBindBodyWith(obj, binding.JSON) +} + +// ShouldBindBodyWithXML is a shortcut for c.ShouldBindBodyWith(obj, binding.XML). +func (c *Context) ShouldBindBodyWithXML(obj any) error { + return c.ShouldBindBodyWith(obj, binding.XML) +} + +// ShouldBindBodyWithYAML is a shortcut for c.ShouldBindBodyWith(obj, binding.YAML). +func (c *Context) ShouldBindBodyWithYAML(obj any) error { + return c.ShouldBindBodyWith(obj, binding.YAML) +} + +// ShouldBindBodyWithTOML is a shortcut for c.ShouldBindBodyWith(obj, binding.TOML). +func (c *Context) ShouldBindBodyWithTOML(obj any) error { + return c.ShouldBindBodyWith(obj, binding.TOML) +} + // ClientIP implements one best effort algorithm to return the real client IP. // It calls 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]). diff --git a/context_test.go b/context_test.go index 9c1717ed20..e9bbae522c 100644 --- a/context_test.go +++ b/context_test.go @@ -1977,6 +1977,263 @@ func TestContextShouldBindBodyWith(t *testing.T) { } } +func TestContextShouldBindBodyWithJSON(t *testing.T) { + for _, tt := range []struct { + name string + bindingBody binding.BindingBody + body string + }{ + { + name: " JSON & JSON-BODY ", + bindingBody: binding.JSON, + body: `{"foo":"FOO"}`, + }, + { + name: " JSON & XML-BODY ", + bindingBody: binding.XML, + body: ` + +FOO +`, + }, + { + name: " JSON & YAML-BODY ", + bindingBody: binding.YAML, + body: `foo: FOO`, + }, + { + name: " JSON & TOM-BODY ", + bindingBody: binding.TOML, + body: `foo=FOO`, + }, + } { + t.Logf("testing: %s", tt.name) + + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + + type typeJSON struct { + Foo string `json:"foo" binding:"required"` + } + objJSON := typeJSON{} + + if tt.bindingBody == binding.JSON { + assert.NoError(t, c.ShouldBindBodyWithJSON(&objJSON)) + assert.Equal(t, typeJSON{"FOO"}, objJSON) + } + + if tt.bindingBody == binding.XML { + assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + assert.Equal(t, typeJSON{}, objJSON) + } + + if tt.bindingBody == binding.YAML { + assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + assert.Equal(t, typeJSON{}, objJSON) + } + + if tt.bindingBody == binding.TOML { + assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + assert.Equal(t, typeJSON{}, objJSON) + } + } +} + +func TestContextShouldBindBodyWithXML(t *testing.T) { + for _, tt := range []struct { + name string + bindingBody binding.BindingBody + body string + }{ + { + name: " XML & JSON-BODY ", + bindingBody: binding.JSON, + body: `{"foo":"FOO"}`, + }, + { + name: " XML & XML-BODY ", + bindingBody: binding.XML, + body: ` + +FOO +`, + }, + { + name: " XML & YAML-BODY ", + bindingBody: binding.YAML, + body: `foo: FOO`, + }, + { + name: " XML & TOM-BODY ", + bindingBody: binding.TOML, + body: `foo=FOO`, + }, + } { + t.Logf("testing: %s", tt.name) + + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + + type typeXML struct { + Foo string `xml:"foo" binding:"required"` + } + objXML := typeXML{} + + if tt.bindingBody == binding.JSON { + assert.Error(t, c.ShouldBindBodyWithXML(&objXML)) + assert.Equal(t, typeXML{}, objXML) + } + + if tt.bindingBody == binding.XML { + assert.NoError(t, c.ShouldBindBodyWithXML(&objXML)) + assert.Equal(t, typeXML{"FOO"}, objXML) + } + + if tt.bindingBody == binding.YAML { + assert.Error(t, c.ShouldBindBodyWithXML(&objXML)) + assert.Equal(t, typeXML{}, objXML) + } + + if tt.bindingBody == binding.TOML { + assert.Error(t, c.ShouldBindBodyWithXML(&objXML)) + assert.Equal(t, typeXML{}, objXML) + } + } +} + +func TestContextShouldBindBodyWithYAML(t *testing.T) { + for _, tt := range []struct { + name string + bindingBody binding.BindingBody + body string + }{ + { + name: " YAML & JSON-BODY ", + bindingBody: binding.JSON, + body: `{"foo":"FOO"}`, + }, + { + name: " YAML & XML-BODY ", + bindingBody: binding.XML, + body: ` + +FOO +`, + }, + { + name: " YAML & YAML-BODY ", + bindingBody: binding.YAML, + body: `foo: FOO`, + }, + { + name: " YAML & TOM-BODY ", + bindingBody: binding.TOML, + body: `foo=FOO`, + }, + } { + t.Logf("testing: %s", tt.name) + + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + + type typeYAML struct { + Foo string `yaml:"foo" binding:"required"` + } + objYAML := typeYAML{} + + // YAML belongs to a super collection of JSON, so JSON can be parsed by YAML + if tt.bindingBody == binding.JSON { + assert.NoError(t, c.ShouldBindBodyWithYAML(&objYAML)) + assert.Equal(t, typeYAML{"FOO"}, objYAML) + } + + if tt.bindingBody == binding.XML { + assert.Error(t, c.ShouldBindBodyWithYAML(&objYAML)) + assert.Equal(t, typeYAML{}, objYAML) + } + + if tt.bindingBody == binding.YAML { + assert.NoError(t, c.ShouldBindBodyWithYAML(&objYAML)) + assert.Equal(t, typeYAML{"FOO"}, objYAML) + } + + if tt.bindingBody == binding.TOML { + assert.Error(t, c.ShouldBindBodyWithYAML(&objYAML)) + assert.Equal(t, typeYAML{}, objYAML) + } + } +} + +func TestContextShouldBindBodyWithTOML(t *testing.T) { + for _, tt := range []struct { + name string + bindingBody binding.BindingBody + body string + }{ + { + name: " TOML & JSON-BODY ", + bindingBody: binding.JSON, + body: `{"foo":"FOO"}`, + }, + { + name: " TOML & XML-BODY ", + bindingBody: binding.XML, + body: ` + +FOO +`, + }, + { + name: " TOML & YAML-BODY ", + bindingBody: binding.YAML, + body: `foo: FOO`, + }, + { + name: " TOML & TOM-BODY ", + bindingBody: binding.TOML, + body: `foo = 'FOO'`, + }, + } { + t.Logf("testing: %s", tt.name) + + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + + type typeTOML struct { + Foo string `toml:"foo" binding:"required"` + } + objTOML := typeTOML{} + + if tt.bindingBody == binding.JSON { + assert.Error(t, c.ShouldBindBodyWithTOML(&objTOML)) + assert.Equal(t, typeTOML{}, objTOML) + } + + if tt.bindingBody == binding.XML { + assert.Error(t, c.ShouldBindBodyWithTOML(&objTOML)) + assert.Equal(t, typeTOML{}, objTOML) + } + + if tt.bindingBody == binding.YAML { + assert.Error(t, c.ShouldBindBodyWithTOML(&objTOML)) + assert.Equal(t, typeTOML{}, objTOML) + } + + if tt.bindingBody == binding.TOML { + assert.NoError(t, c.ShouldBindBodyWithTOML(&objTOML)) + assert.Equal(t, typeTOML{"FOO"}, objTOML) + } + } +} + func TestContextGolangContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) diff --git a/docs/doc.md b/docs/doc.md index df006e87a7..70c9f27540 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -1956,7 +1956,12 @@ func SomeHandler(c *gin.Context) { } ``` -For this, you can use `c.ShouldBindBodyWith`. +For this, you can use `c.ShouldBindBodyWith` or shortcuts. + +- `c.ShouldBindBodyWithJSON` is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON). +- `c.ShouldBindBodyWithXML` is a shortcut for c.ShouldBindBodyWith(obj, binding.XML). +- `c.ShouldBindBodyWithYAML` is a shortcut for c.ShouldBindBodyWith(obj, binding.YAML). +- `c.ShouldBindBodyWithTOML` is a shortcut for c.ShouldBindBodyWith(obj, binding.TOML). ```go func SomeHandler(c *gin.Context) { @@ -1969,7 +1974,7 @@ func SomeHandler(c *gin.Context) { } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil { c.String(http.StatusOK, `the body should be formB JSON`) // And it can accepts other formats - } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil { + } else if errB2 := c.ShouldBindBodyWithXML(&objB); errB2 == nil { c.String(http.StatusOK, `the body should be formB XML`) } else { ... From c964ad370bbe007f1b18a7570f058a66f05fbe1f Mon Sep 17 00:00:00 2001 From: Name <1911860538@qq.com> Date: Mon, 1 Apr 2024 12:58:01 +0800 Subject: [PATCH 292/346] chore(optimize): the ShouldBindUri method of the Context struct (#3911) Co-authored-by: huangzw --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index afc3c353e1..391adafed0 100644 --- a/context.go +++ b/context.go @@ -739,7 +739,7 @@ func (c *Context) ShouldBindHeader(obj any) error { // ShouldBindUri binds the passed struct pointer using the specified binding engine. func (c *Context) ShouldBindUri(obj any) error { - m := make(map[string][]string) + m := make(map[string][]string, len(c.Params)) for _, v := range c.Params { m[v.Key] = []string{v.Value} } From 56dc72c4d5b1076fc9c6b81f57299739c11910b8 Mon Sep 17 00:00:00 2001 From: imalasong <55082705+imalasong@users.noreply.github.com> Date: Tue, 2 Apr 2024 11:58:00 +0800 Subject: [PATCH 293/346] ci(Makefile): vet command add .PHONY (#3915) --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index ebde4ee840..b58f24f32b 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,7 @@ fmt-check: exit 1; \ fi; +.PHONY: vet vet: $(GO) vet $(VETPACKAGES) From 8acbe657f1c140e3fba38f869978cab2376500c9 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 2 Apr 2024 16:20:48 +0800 Subject: [PATCH 294/346] ci(release): refactor changelog regex patterns and exclusions (#3914) * chore: refactor changelog regex patterns and exclusions - Update the build configuration to skip the build using a comment - Change the `changelog` use from `git` to `github` - Modify the regex patterns for `Features`, `Bug fixes`, and `Enhancements` titles in the changelog - Add a new regex pattern for the `Refactor` title in the changelog - Update the excluded items in the changelog to include `docs` and `CICD` with corrected quotes Signed-off-by: appleboy * chore: update configuration file field names - Change the `skip` field to `disable` in the `.goreleaser.yaml` file. Signed-off-by: appleboy * update Signed-off-by: appleboy * chore: refine changelog categorization rules - Update regular expressions for `feat`, `fix`, and `chore` categories in `.goreleaser.yaml` - Remove `Refactor` category from changelog configuration - Add `Refactor` category with updated regular expression and order to changelog configuration Signed-off-by: Bo-Yi Wu --------- Signed-off-by: appleboy Signed-off-by: Bo-Yi Wu --- .goreleaser.yaml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index e435e56aa5..1cc0bee153 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,8 +1,7 @@ project_name: gin builds: - - - # If true, skip the build. + - # If true, skip the build. # Useful for library projects. # Default is false skip: true @@ -10,7 +9,7 @@ builds: changelog: # Set it to true if you wish to skip the changelog generation. # This may result in an empty release notes on GitHub/GitLab/Gitea. - skip: false + disable: false # Changelog generation implementation to use. # @@ -21,7 +20,7 @@ changelog: # - `github-native`: uses the GitHub release notes generation API, disables the groups feature. # # Defaults to `git`. - use: git + use: github # Sorts the changelog by the commit's messages. # Could either be asc, desc or empty @@ -38,12 +37,15 @@ changelog: - title: Features regexp: "^.*feat[(\\w)]*:+.*$" order: 0 - - title: 'Bug fixes' + - title: "Bug fixes" regexp: "^.*fix[(\\w)]*:+.*$" order: 1 - - title: 'Enhancements' + - title: "Enhancements" regexp: "^.*chore[(\\w)]*:+.*$" order: 2 + - title: "Refactor" + regexp: "^.*refactor[(\\w)]*:+.*$" + order: 3 - title: Others order: 999 @@ -52,6 +54,6 @@ changelog: # the changelog # Default is empty exclude: - - '^docs' - - 'CICD' + - "^docs" + - "CICD" - typo From c6f90df4e0c888c69524307cc35952ec2e7ead41 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 2 Apr 2024 18:57:22 +0800 Subject: [PATCH 295/346] chore: update various Go dependencies to latest versions (#3901) - Update `github.com/bytedance/sonic` from v1.11.0 to v1.11.3 - Update `github.com/go-playground/validator/v10` from v10.18.0 to v10.19.0 - Update `github.com/pelletier/go-toml/v2` from v2.1.1 to v2.2.0 - Update `github.com/stretchr/testify` from v1.8.4 to v1.9.0 - Update `golang.org/x/net` from v0.21.0 to v0.22.0 - Update `golang.org/x/crypto` from v0.19.0 to v0.21.0 - Update `golang.org/x/sys` from v0.17.0 to v0.18.0 Signed-off-by: appleboy --- go.mod | 16 ++++++++-------- go.sum | 32 +++++++++++++++++--------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index 11ce23e80e..13342ac2de 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,16 @@ module github.com/gin-gonic/gin go 1.20 require ( - github.com/bytedance/sonic v1.11.0 + github.com/bytedance/sonic v1.11.3 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.18.0 + github.com/go-playground/validator/v10 v10.19.0 github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.20 - github.com/pelletier/go-toml/v2 v2.1.1 - github.com/stretchr/testify v1.8.4 + github.com/pelletier/go-toml/v2 v2.2.0 + github.com/stretchr/testify v1.9.0 github.com/ugorji/go/codec v1.2.12 - golang.org/x/net v0.21.0 + golang.org/x/net v0.22.0 google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -24,14 +24,14 @@ require ( github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.7.0 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index 49eae34304..7a3aa22c42 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.0 h1:FwNNv6Vu4z2Onf1++LNzxB/QhitD8wuTdpZzMTGITWo= -github.com/bytedance/sonic v1.11.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= +github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= @@ -21,8 +21,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= -github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= @@ -30,8 +30,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= -github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= @@ -42,20 +42,22 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= +github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= @@ -63,14 +65,14 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= From 0397e5e0c0f8f8176c29f7edd8f1bff8e45df780 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 7 Apr 2024 10:18:23 +0800 Subject: [PATCH 296/346] chore: update changelog categories and improve documentation (#3917) - Add new changelog categories for "Build process updates" and "Documentation updates" Signed-off-by: appleboy --- .goreleaser.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 1cc0bee153..ac2b462b3a 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -46,6 +46,12 @@ changelog: - title: "Refactor" regexp: "^.*refactor[(\\w)]*:+.*$" order: 3 + - title: "Build process updates" + regexp: ^.*?(build|ci)(\(.+\))??!?:.+$ + order: 4 + - title: "Documentation updates" + regexp: ^.*?docs?(\(.+\))??!?:.+$ + order: 4 - title: Others order: 999 From f80ade7a4b85b116d294c7610a89819905fa44d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 07:07:01 +0800 Subject: [PATCH 297/346] chore(deps): bump golangci/golangci-lint-action from 4 to 5 (#3941) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 4 to 5. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v4...v5) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 3fe007f16a..2e434341b8 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -25,7 +25,7 @@ jobs: go-version-file: "go.mod" check-latest: true - name: Setup golangci-lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v5 with: version: v1.56.2 args: --verbose From b4f66e965ba9d60257e0de4c25d4ad4bd6115927 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 07:07:23 +0800 Subject: [PATCH 298/346] chore(deps): bump github.com/bytedance/sonic from 1.11.3 to 1.11.6 (#3940) Bumps [github.com/bytedance/sonic](https://github.com/bytedance/sonic) from 1.11.3 to 1.11.6. - [Release notes](https://github.com/bytedance/sonic/releases) - [Commits](https://github.com/bytedance/sonic/compare/v1.11.3...v1.11.6) --- updated-dependencies: - dependency-name: github.com/bytedance/sonic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 7 ++++--- go.sum | 19 ++++++++----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 13342ac2de..44e02a303d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.20 require ( - github.com/bytedance/sonic v1.11.3 + github.com/bytedance/sonic v1.11.6 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.19.0 github.com/goccy/go-json v0.10.2 @@ -18,8 +18,9 @@ require ( ) require ( - github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect - github.com/chenzhuoyu/iasm v0.9.1 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-playground/locales v0.14.1 // indirect diff --git a/go.sum b/go.sum index 7a3aa22c42..2028e88ee2 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,11 @@ -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= -github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= -github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= -github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From a18219566ca25fc51e6d2886bed849c6c3a0cd12 Mon Sep 17 00:00:00 2001 From: dkkb <82504881+dkkb@users.noreply.github.com> Date: Tue, 7 May 2024 09:43:15 +0800 Subject: [PATCH 299/346] feat(binding): Support custom BindUnmarshaler for binding. (#3933) --- binding/form_mapping.go | 20 ++++++++ binding/form_mapping_test.go | 99 ++++++++++++++++++++++++++++++++++++ docs/doc.md | 41 +++++++++++++++ gin_test.go | 24 +++++++++ 4 files changed, 184 insertions(+) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 77a1bde697..db235e5661 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -165,6 +165,23 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter return setter.TrySet(value, field, tagValue, setOpt) } +// BindUnmarshaler is the interface used to wrap the UnmarshalParam method. +type BindUnmarshaler interface { + // UnmarshalParam decodes and assigns a value from an form or query param. + UnmarshalParam(param string) error +} + +// trySetCustom tries to set a custom type value +// If the value implements the BindUnmarshaler interface, it will be used to set the value, we will return `true` +// to skip the default value setting. +func trySetCustom(val string, value reflect.Value) (isSet bool, err error) { + switch v := value.Addr().Interface().(type) { + case BindUnmarshaler: + return true, v.UnmarshalParam(val) + } + return false, nil +} + func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSet bool, err error) { vs, ok := form[tagValue] if !ok && !opt.isDefaultExists { @@ -194,6 +211,9 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][ if len(vs) > 0 { val = vs[0] } + if ok, err := trySetCustom(val, value); ok { + return ok, err + } return true, setWithProperType(val, value, field) } } diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 16527eb916..ed01a08641 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -5,8 +5,11 @@ package binding import ( + "fmt" "mime/multipart" "reflect" + "strconv" + "strings" "testing" "time" @@ -323,3 +326,99 @@ func TestMappingIgnoredCircularRef(t *testing.T) { err := mappingByPtr(&s, formSource{}, "form") assert.NoError(t, err) } + +type customUnmarshalParamHex int + +func (f *customUnmarshalParamHex) UnmarshalParam(param string) error { + v, err := strconv.ParseInt(param, 16, 64) + if err != nil { + return err + } + *f = customUnmarshalParamHex(v) + return nil +} + +func TestMappingCustomUnmarshalParamHexWithFormTag(t *testing.T) { + var s struct { + Foo customUnmarshalParamHex `form:"foo"` + } + err := mappingByPtr(&s, formSource{"foo": {`f5`}}, "form") + assert.NoError(t, err) + + assert.EqualValues(t, 245, s.Foo) +} + +func TestMappingCustomUnmarshalParamHexWithURITag(t *testing.T) { + var s struct { + Foo customUnmarshalParamHex `uri:"foo"` + } + err := mappingByPtr(&s, formSource{"foo": {`f5`}}, "uri") + assert.NoError(t, err) + + assert.EqualValues(t, 245, s.Foo) +} + +type customUnmarshalParamType struct { + Protocol string + Path string + Name string +} + +func (f *customUnmarshalParamType) UnmarshalParam(param string) error { + parts := strings.Split(param, ":") + if len(parts) != 3 { + return fmt.Errorf("invalid format") + } + f.Protocol = parts[0] + f.Path = parts[1] + f.Name = parts[2] + return nil +} + +func TestMappingCustomStructTypeWithFormTag(t *testing.T) { + var s struct { + FileData customUnmarshalParamType `form:"data"` + } + err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form") + assert.NoError(t, err) + + assert.EqualValues(t, "file", s.FileData.Protocol) + assert.EqualValues(t, "/foo", s.FileData.Path) + assert.EqualValues(t, "happiness", s.FileData.Name) +} + +func TestMappingCustomStructTypeWithURITag(t *testing.T) { + var s struct { + FileData customUnmarshalParamType `uri:"data"` + } + err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri") + assert.NoError(t, err) + + assert.EqualValues(t, "file", s.FileData.Protocol) + assert.EqualValues(t, "/foo", s.FileData.Path) + assert.EqualValues(t, "happiness", s.FileData.Name) +} + +func TestMappingCustomPointerStructTypeWithFormTag(t *testing.T) { + var s struct { + FileData *customUnmarshalParamType `form:"data"` + } + err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form") + assert.NoError(t, err) + + assert.EqualValues(t, "file", s.FileData.Protocol) + assert.EqualValues(t, "/foo", s.FileData.Path) + assert.EqualValues(t, "happiness", s.FileData.Name) +} + +func TestMappingCustomPointerStructTypeWithURITag(t *testing.T) { + var s struct { + FileData *customUnmarshalParamType `uri:"data"` + } + err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri") + assert.NoError(t, err) + + assert.EqualValues(t, "file", s.FileData.Protocol) + assert.EqualValues(t, "/foo", s.FileData.Path) + assert.EqualValues(t, "happiness", s.FileData.Name) +} diff --git a/docs/doc.md b/docs/doc.md index 70c9f27540..177c4471e8 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -27,6 +27,7 @@ - [Only Bind Query String](#only-bind-query-string) - [Bind Query String or Post Data](#bind-query-string-or-post-data) - [Bind Uri](#bind-uri) + - [Bind custom unmarshaler](#bind-custom-unmarshaler) - [Bind Header](#bind-header) - [Bind HTML checkboxes](#bind-html-checkboxes) - [Multipart/Urlencoded binding](#multiparturlencoded-binding) @@ -899,6 +900,46 @@ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3 curl -v localhost:8088/thinkerou/not-uuid ``` +### Bind custom unmarshaler + +```go +package main + +import ( + "github.com/gin-gonic/gin" + "strings" +) + +type Birthday string + +func (b *Birthday) UnmarshalParam(param string) error { + *b = Birthday(strings.Replace(param, "-", "/", -1)) + return nil +} + +func main() { + route := gin.Default() + var request struct { + Birthday Birthday `form:"birthday"` + } + route.GET("/test", func(ctx *gin.Context) { + _ = ctx.BindQuery(&request) + ctx.JSON(200, request.Birthday) + }) + route.Run(":8088") +} +``` + +Test it with: + +```sh +curl 'localhost:8088/test?birthday=2000-01-01' +``` +Result +```sh +"2000/01/01" +``` + ### Bind Header ```go diff --git a/gin_test.go b/gin_test.go index 4550a7e5c8..e68f1ce8fd 100644 --- a/gin_test.go +++ b/gin_test.go @@ -14,6 +14,7 @@ import ( "net/http/httptest" "reflect" "strconv" + "strings" "sync/atomic" "testing" "time" @@ -730,3 +731,26 @@ func TestWithOptionFunc(t *testing.T) { assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"}) assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"}) } + +type Birthday string + +func (b *Birthday) UnmarshalParam(param string) error { + *b = Birthday(strings.Replace(param, "-", "/", -1)) + return nil +} + +func TestCustomUnmarshalStruct(t *testing.T) { + route := Default() + var request struct { + Birthday Birthday `form:"birthday"` + } + route.GET("/test", func(ctx *Context) { + _ = ctx.BindQuery(&request) + ctx.JSON(200, request.Birthday) + }) + req := httptest.NewRequest("GET", "/test?birthday=2000-01-01", nil) + w := httptest.NewRecorder() + route.ServeHTTP(w, req) + assert.Equal(t, 200, w.Code) + assert.Equal(t, `"2000/01/01"`, w.Body.String()) +} From 638aa19e7d30513f7bc777c62ff8558fd5f90ea5 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 7 May 2024 10:15:53 +0800 Subject: [PATCH 300/346] chore: update external dependencies to latest versions (#3950) - Update `github.com/go-playground/validator/v10` from v10.19.0 to v10.20.0 - Update `github.com/pelletier/go-toml/v2` from v2.2.0 to v2.2.2 - Update `golang.org/x/net` from v0.22.0 to v0.25.0 - Update `google.golang.org/protobuf` from v1.33.0 to v1.34.1 - Update `golang.org/x/arch` from v0.7.0 to v0.8.0 - Update `golang.org/x/crypto` from v0.21.0 to v0.23.0 - Update `golang.org/x/sys` from v0.18.0 to v0.20.0 - Update `golang.org/x/text` from v0.14.0 to v0.15.0 Signed-off-by: Bo-Yi Wu --- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 44e02a303d..3e94e50585 100644 --- a/go.mod +++ b/go.mod @@ -5,15 +5,15 @@ go 1.20 require ( github.com/bytedance/sonic v1.11.6 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.19.0 + github.com/go-playground/validator/v10 v10.20.0 github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.20 - github.com/pelletier/go-toml/v2 v2.2.0 + github.com/pelletier/go-toml/v2 v2.2.2 github.com/stretchr/testify v1.9.0 github.com/ugorji/go/codec v1.2.12 - golang.org/x/net v0.22.0 - google.golang.org/protobuf v1.33.0 + golang.org/x/net v0.25.0 + google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -31,8 +31,8 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - golang.org/x/arch v0.7.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect ) diff --git a/go.sum b/go.sum index 2028e88ee2..ce905e701b 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= -github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= @@ -39,8 +39,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= -github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -60,21 +60,21 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2 github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= -golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 39089af62535b27aa63608f341c0a339aa88f64e Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 7 May 2024 10:16:38 +0800 Subject: [PATCH 301/346] chore: refactor configuration files for better readability (#3951) - Remove filters from the `changelog` section in `.goreleaser.yaml` Signed-off-by: Bo-Yi Wu --- .goreleaser.yaml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index ac2b462b3a..99b66fee71 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -54,12 +54,3 @@ changelog: order: 4 - title: Others order: 999 - - filters: - # Commit messages matching the regexp listed here will be removed from - # the changelog - # Default is empty - exclude: - - "^docs" - - "CICD" - - typo From 75ccf94d605a05fe24817fc2f166f6f2959d5cea Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 7 May 2024 11:23:42 +0800 Subject: [PATCH 302/346] feat: update version constant to v1.10.0 (#3952) - Update the version constant from "v1.9.1" to "v1.10.0" Signed-off-by: Bo-Yi Wu --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 85462e5553..93ad965417 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.9.1" +const Version = "v1.10.0" From 490accf5d7d49138f0af806318826d92513b1395 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 7 May 2024 12:50:01 +0800 Subject: [PATCH 303/346] docs: update documentation and release notes for Gin v1.10.0 (#3953) * docs: update documentation and release notes for Gin v1.10.0 - Add release notes for Gin v1.10.0 - Include new features and bug fixes in the changelog - Document enhancements and build process updates - Update documentation for context and middleware functions - Upgrade dependencies and optimize unit tests Signed-off-by: Bo-Yi Wu * feat: refactor CI, enhance file binding, and update dependencies - Add proxy-server authentication feature - Add support for custom BindUnmarshaler for binding - Fix binding error while not uploading file - Refactor CI and update dependencies - Add support for RFC 9512: application/yaml - Optimize the Copy method of the Context struct - Update various Go dependencies to latest versions Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu --- CHANGELOG.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7968520553..de47c75018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,82 @@ # Gin ChangeLog +## Gin v1.10.0 + +### Features + +* feat(auth): add proxy-server authentication (#3877) (@EndlessParadox1) +* feat(bind): ShouldBindBodyWith shortcut and change doc (#3871) (@RedCrazyGhost) +* feat(binding): Support custom BindUnmarshaler for binding. (#3933) (@dkkb) +* feat(binding): support override default binding implement (#3514) (@ssfyn) +* feat(engine): Added `OptionFunc` and `With` (#3572) (@flc1125) +* feat(logger): ability to skip logs based on user-defined logic (#3593) (@palvaneh) + +### Bug fixes + +* Revert "fix(uri): query binding bug (#3236)" (#3899) (@appleboy) +* fix(binding): binding error while not upload file (#3819) (#3820) (@clearcodecn) +* fix(binding): dereference pointer to struct (#3199) (@echovl) +* fix(context): make context Value method adhere to Go standards (#3897) (@FarmerChillax) +* fix(engine): fix unit test (#3878) (@flc1125) +* fix(header): Allow header according to RFC 7231 (HTTP 405) (#3759) (@Crocmagnon) +* fix(route): Add fullPath in context copy (#3784) (@KarthikReddyPuli) +* fix(router): catch-all conflicting wildcard (#3812) (@FirePing32) +* fix(sec): upgrade golang.org/x/crypto to 0.17.0 (#3832) (@chncaption) +* fix(tree): correctly expand the capacity of params (#3502) (@georgijd-form3) +* fix(uri): query binding bug (#3236) (@illiafox) +* fix: Add pointer support for url query params (#3659) (#3666) (@omkar-foss) +* fix: protect Context.Keys map when call Copy method (#3873) (@kingcanfish) + +### Enhancements + +* chore(CI): update release args (#3595) (@qloog) +* chore(IP): add TrustedPlatform constant for Fly.io. (#3839) (@ab) +* chore(debug): add ability to override the debugPrint statement (#2337) (@josegonzalez) +* chore(deps): update dependencies to latest versions (#3835) (@appleboy) +* chore(header): Add support for RFC 9512: application/yaml (#3851) (@vincentbernat) +* chore(http): use white color for HTTP 1XX (#3741) (@viralparmarme) +* chore(optimize): the ShouldBindUri method of the Context struct (#3911) (@1911860538) +* chore(perf): Optimize the Copy method of the Context struct (#3859) (@1911860538) +* chore(refactor): modify interface check way (#3855) (@demoManito) +* chore(request): check reader if it's nil before reading (#3419) (@noahyao1024) +* chore(security): upgrade Protobuf for CVE-2024-24786 (#3893) (@Fotkurz) +* chore: refactor CI and update dependencies (#3848) (@appleboy) +* chore: refactor configuration files for better readability (#3951) (@appleboy) +* chore: update GitHub Actions configuration (#3792) (@appleboy) +* chore: update changelog categories and improve documentation (#3917) (@appleboy) +* chore: update dependencies to latest versions (#3694) (@appleboy) +* chore: update external dependencies to latest versions (#3950) (@appleboy) +* chore: update various Go dependencies to latest versions (#3901) (@appleboy) + +### Build process updates + +* build(codecov): Added a codecov configuration (#3891) (@flc1125) +* ci(Makefile): vet command add .PHONY (#3915) (@imalasong) +* ci(lint): update tooling and workflows for consistency (#3834) (@appleboy) +* ci(release): refactor changelog regex patterns and exclusions (#3914) (@appleboy) +* ci(testing): add go1.22 version (#3842) (@appleboy) + +### Documentation updates + +* docs(context): Added deprecation comments to BindWith (#3880) (@flc1125) +* docs(middleware): comments to function `BasicAuthForProxy` (#3881) (@EndlessParadox1) +* docs: Add document to constant `AuthProxyUserKey` and `BasicAuthForProxy`. (#3887) (@EndlessParadox1) +* docs: fix typo in comment (#3868) (@testwill) +* docs: fix typo in function documentation (#3872) (@TotomiEcio) +* docs: remove redundant comments (#3765) (@WeiTheShinobi) +* feat: update version constant to v1.10.0 (#3952) (@appleboy) + +### Others + +* Upgrade golang.org/x/net -> v0.13.0 (#3684) (@cpcf) +* test(git): gitignore add develop tools (#3370) (@demoManito) +* test(http): use constant instead of numeric literal (#3863) (@testwill) +* test(path): Optimize unit test execution results (#3883) (@flc1125) +* test(render): increased unit tests coverage (#3691) (@araujo88) + ## Gin v1.9.1 -### BUG FIXES +### BUG FIXES * fix Request.Context() checks [#3512](https://github.com/gin-gonic/gin/pull/3512) From e60113dc9531779a15dcb761655d986a56273ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Flc=E3=82=9B?= Date: Wed, 8 May 2024 05:29:54 +0800 Subject: [PATCH 304/346] docs(engine): fix comments for the `With` (#3955) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Flc゛ --- gin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 1633fe13f9..8f32378801 100644 --- a/gin.go +++ b/gin.go @@ -316,7 +316,7 @@ func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { return engine } -// With returns a new Engine instance with the provided options. +// With returns a Engine with the configuration set in the OptionFunc. func (engine *Engine) With(opts ...OptionFunc) *Engine { for _, opt := range opts { opt(engine) From 8dd088927ab50b3b37be1f7ba14931c8eddafe07 Mon Sep 17 00:00:00 2001 From: lgbgbl <65756378+lgbgbl@users.noreply.github.com> Date: Wed, 8 May 2024 06:28:15 +0800 Subject: [PATCH 305/346] refactor(binding): use strings.Cut to replace strings.Index (#3522) --- binding/form_mapping.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index db235e5661..108606fa56 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -397,11 +397,8 @@ func setTimeDuration(val string, value reflect.Value) error { } func head(str, sep string) (head string, tail string) { - idx := strings.Index(str, sep) - if idx < 0 { - return str, "" - } - return str[:idx], str[idx+len(sep):] + head, tail, _ = strings.Cut(str, sep) + return head, tail } func setFormMap(ptr any, form map[string][]string) error { From f5f5da8fa09d12a22225c493fa8191fb14bdd5bf Mon Sep 17 00:00:00 2001 From: Pedro Aguiar <72931357+codespearhead@users.noreply.github.com> Date: Tue, 7 May 2024 18:31:01 -0400 Subject: [PATCH 306/346] docs(gin): update link to dont-trust-all-proxies section (#3938) (#3945) Update link [1] to [2] after PR #3449 was merged. [1] https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies [2] https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies Closes --- gin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gin.go b/gin.go index 8f32378801..03edbfff2f 100644 --- a/gin.go +++ b/gin.go @@ -391,7 +391,7 @@ func (engine *Engine) Run(addr ...string) (err error) { if engine.isUnsafeTrustedProxies() { debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + - "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") } address := resolveAddress(addr) @@ -512,7 +512,7 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) { if engine.isUnsafeTrustedProxies() { debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + - "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") } err = http.ListenAndServeTLS(addr, certFile, keyFile, engine.Handler()) From 7d147928ee232fce156ea7ce8ae6329e148aeb41 Mon Sep 17 00:00:00 2001 From: Kostadin Plachkov <20207730+kplachkov@users.noreply.github.com> Date: Wed, 8 May 2024 04:13:36 +0300 Subject: [PATCH 307/346] fix(gin): data race warning for gin mode (#1580) * fix: data race warning (#1180) * Fix the tests * refactor: remove unnecessary imports and optimize codebase - Remove unnecessary import of `flag` Signed-off-by: Bo-Yi Wu * test: refactor test assertions for mode settings - Update test assertions for mode setting in `mode_test.go` Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu Co-authored-by: Bo-Yi Wu --- debug.go | 3 ++- mode.go | 20 +++++++++----------- mode_test.go | 19 ++++++------------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/debug.go b/debug.go index 1761fe325e..0d808f1090 100644 --- a/debug.go +++ b/debug.go @@ -10,6 +10,7 @@ import ( "runtime" "strconv" "strings" + "sync/atomic" ) const ginSupportMinGoVer = 18 @@ -17,7 +18,7 @@ const ginSupportMinGoVer = 18 // IsDebugging returns true if the framework is running in debug mode. // Use SetMode(gin.ReleaseMode) to disable debug mode. func IsDebugging() bool { - return ginMode == debugCode + return atomic.LoadInt32(&ginMode) == debugCode } // DebugPrintRouteFunc indicates debug log output format. diff --git a/mode.go b/mode.go index fd26d907cc..13aa3be08e 100644 --- a/mode.go +++ b/mode.go @@ -8,6 +8,7 @@ import ( "flag" "io" "os" + "sync/atomic" "github.com/gin-gonic/gin/binding" ) @@ -43,10 +44,8 @@ var DefaultWriter io.Writer = os.Stdout // DefaultErrorWriter is the default io.Writer used by Gin to debug errors var DefaultErrorWriter io.Writer = os.Stderr -var ( - ginMode = debugCode - modeName = DebugMode -) +var ginMode int32 = debugCode +var modeName atomic.Value func init() { mode := os.Getenv(EnvGinMode) @@ -64,17 +63,16 @@ func SetMode(value string) { } switch value { - case DebugMode: - ginMode = debugCode + case DebugMode, "": + atomic.StoreInt32(&ginMode, debugCode) case ReleaseMode: - ginMode = releaseCode + atomic.StoreInt32(&ginMode, releaseCode) case TestMode: - ginMode = testCode + atomic.StoreInt32(&ginMode, testCode) default: panic("gin mode unknown: " + value + " (available mode: debug release test)") } - - modeName = value + modeName.Store(value) } // DisableBindValidation closes the default validator. @@ -96,5 +94,5 @@ func EnableJsonDecoderDisallowUnknownFields() { // Mode returns current gin mode. func Mode() string { - return modeName + return modeName.Load().(string) } diff --git a/mode_test.go b/mode_test.go index 2407f46339..be03a9d058 100644 --- a/mode_test.go +++ b/mode_test.go @@ -5,8 +5,8 @@ package gin import ( - "flag" "os" + "sync/atomic" "testing" "github.com/gin-gonic/gin/binding" @@ -18,31 +18,24 @@ func init() { } func TestSetMode(t *testing.T) { - assert.Equal(t, testCode, ginMode) + assert.Equal(t, int32(testCode), atomic.LoadInt32(&ginMode)) assert.Equal(t, TestMode, Mode()) os.Unsetenv(EnvGinMode) SetMode("") - assert.Equal(t, testCode, ginMode) + assert.Equal(t, int32(testCode), atomic.LoadInt32(&ginMode)) assert.Equal(t, TestMode, Mode()) - tmp := flag.CommandLine - flag.CommandLine = flag.NewFlagSet("", flag.ContinueOnError) - SetMode("") - assert.Equal(t, debugCode, ginMode) - assert.Equal(t, DebugMode, Mode()) - flag.CommandLine = tmp - SetMode(DebugMode) - assert.Equal(t, debugCode, ginMode) + assert.Equal(t, int32(debugCode), atomic.LoadInt32(&ginMode)) assert.Equal(t, DebugMode, Mode()) SetMode(ReleaseMode) - assert.Equal(t, releaseCode, ginMode) + assert.Equal(t, int32(releaseCode), atomic.LoadInt32(&ginMode)) assert.Equal(t, ReleaseMode, Mode()) SetMode(TestMode) - assert.Equal(t, testCode, ginMode) + assert.Equal(t, int32(testCode), atomic.LoadInt32(&ginMode)) assert.Equal(t, TestMode, Mode()) assert.Panics(t, func() { SetMode("unknown") }) From b1c1e7b572f76071fb0e0e7884a0697e0458aa7c Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Wed, 8 May 2024 10:14:42 +0800 Subject: [PATCH 308/346] ci: update Go version requirements and remove test files (#3957) - Update the Go version requirements in `.github/workflows/gin.yml` - Remove test files for Go versions 1.18 and 1.19 - Update the required Go version in `debug.go` and `debug_test.go` - Rename and modify files related to Go version 1.19 and 1.20 in the `internal/bytesconv` directory Signed-off-by: Bo-Yi Wu --- .github/workflows/gin.yml | 2 +- context_1.18_test.go | 37 ------------------- context_1.19_test.go | 30 --------------- context_test.go | 13 +++++++ debug.go | 2 +- debug_test.go | 2 +- .../{bytesconv_1.20.go => bytesconv.go} | 2 - internal/bytesconv/bytesconv_1.19.go | 26 ------------- 8 files changed, 16 insertions(+), 98 deletions(-) delete mode 100644 context_1.18_test.go delete mode 100644 context_1.19_test.go rename internal/bytesconv/{bytesconv_1.20.go => bytesconv.go} (97%) delete mode 100644 internal/bytesconv/bytesconv_1.19.go diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 2e434341b8..4a9dbc9368 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -34,7 +34,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: ["1.18", "1.19", "1.20", "1.21", "1.22"] + go: ["1.20", "1.21", "1.22"] test-tags: ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json", "-race"] include: diff --git a/context_1.18_test.go b/context_1.18_test.go deleted file mode 100644 index 6118beaa49..0000000000 --- a/context_1.18_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2021 Gin Core Team. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build !go1.19 - -package gin - -import ( - "bytes" - "mime/multipart" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestContextFormFileFailed18(t *testing.T) { - buf := new(bytes.Buffer) - mw := multipart.NewWriter(buf) - defer func(mw *multipart.Writer) { - err := mw.Close() - if err != nil { - assert.Error(t, err) - } - }(mw) - c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) - c.Request.Header.Set("Content-Type", mw.FormDataContentType()) - c.engine.MaxMultipartMemory = 8 << 20 - assert.Panics(t, func() { - f, err := c.FormFile("file") - assert.Error(t, err) - assert.Nil(t, f) - }) -} diff --git a/context_1.19_test.go b/context_1.19_test.go deleted file mode 100644 index dd75325b18..0000000000 --- a/context_1.19_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2022 Gin Core Team. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build go1.19 - -package gin - -import ( - "bytes" - "mime/multipart" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestContextFormFileFailed19(t *testing.T) { - buf := new(bytes.Buffer) - mw := multipart.NewWriter(buf) - mw.Close() - c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) - c.Request.Header.Set("Content-Type", mw.FormDataContentType()) - c.engine.MaxMultipartMemory = 8 << 20 - f, err := c.FormFile("file") - assert.Error(t, err) - assert.Nil(t, f) -} diff --git a/context_test.go b/context_test.go index e9bbae522c..ae34c6592e 100644 --- a/context_test.go +++ b/context_test.go @@ -90,6 +90,19 @@ func TestContextFormFile(t *testing.T) { assert.NoError(t, c.SaveUploadedFile(f, "test")) } +func TestContextFormFileFailed(t *testing.T) { + buf := new(bytes.Buffer) + mw := multipart.NewWriter(buf) + mw.Close() + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request.Header.Set("Content-Type", mw.FormDataContentType()) + c.engine.MaxMultipartMemory = 8 << 20 + f, err := c.FormFile("file") + assert.Error(t, err) + assert.Nil(t, f) +} + func TestContextMultipartForm(t *testing.T) { buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) diff --git a/debug.go b/debug.go index 0d808f1090..ae346e9c38 100644 --- a/debug.go +++ b/debug.go @@ -78,7 +78,7 @@ func getMinVer(v string) (uint64, error) { func debugPrintWARNINGDefault() { if v, e := getMinVer(runtime.Version()); e == nil && v < ginSupportMinGoVer { - debugPrint(`[WARNING] Now Gin requires Go 1.18+. + debugPrint(`[WARNING] Now Gin requires Go 1.20+. `) } diff --git a/debug_test.go b/debug_test.go index 2d5e9a5600..e39097298a 100644 --- a/debug_test.go +++ b/debug_test.go @@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { }) m, e := getMinVer(runtime.Version()) if e == nil && m < ginSupportMinGoVer { - assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.18+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) + assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.20+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } else { assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } diff --git a/internal/bytesconv/bytesconv_1.20.go b/internal/bytesconv/bytesconv.go similarity index 97% rename from internal/bytesconv/bytesconv_1.20.go rename to internal/bytesconv/bytesconv.go index 5b6040a6b3..a02c53c399 100644 --- a/internal/bytesconv/bytesconv_1.20.go +++ b/internal/bytesconv/bytesconv.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. -//go:build go1.20 - package bytesconv import ( diff --git a/internal/bytesconv/bytesconv_1.19.go b/internal/bytesconv/bytesconv_1.19.go deleted file mode 100644 index 669c9c914e..0000000000 --- a/internal/bytesconv/bytesconv_1.19.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020 Gin Core Team. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build !go1.20 - -package bytesconv - -import ( - "unsafe" -) - -// StringToBytes converts string to byte slice without a memory allocation. -func StringToBytes(s string) []byte { - return *(*[]byte)(unsafe.Pointer( - &struct { - string - Cap int - }{s, len(s)}, - )) -} - -// BytesToString converts byte slice to string without a memory allocation. -func BytesToString(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) -} From 8791c96960e719ff2f41e24163c5898656cee474 Mon Sep 17 00:00:00 2001 From: Johannes Eiglsperger Date: Wed, 8 May 2024 09:47:54 +0200 Subject: [PATCH 309/346] feat(fs): Export, test and document OnlyFilesFS (#3939) --- fs.go | 50 +++++++++++++++++++---------------- fs_test.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ routergroup.go | 2 +- 3 files changed, 100 insertions(+), 23 deletions(-) create mode 100644 fs_test.go diff --git a/fs.go b/fs.go index f17d7434d9..51c3db86fb 100644 --- a/fs.go +++ b/fs.go @@ -9,37 +9,43 @@ import ( "os" ) -type onlyFilesFS struct { - fs http.FileSystem +// OnlyFilesFS implements an http.FileSystem without `Readdir` functionality. +type OnlyFilesFS struct { + FileSystem http.FileSystem } -type neuteredReaddirFile struct { +// Open passes `Open` to the upstream implementation without `Readdir` functionality. +func (o OnlyFilesFS) Open(name string) (http.File, error) { + f, err := o.FileSystem.Open(name) + + if err != nil { + return nil, err + } + + return neutralizedReaddirFile{f}, nil +} + +// neutralizedReaddirFile wraps http.File with a specific implementation of `Readdir`. +type neutralizedReaddirFile struct { http.File } -// Dir returns a http.FileSystem that can be used by http.FileServer(). It is used internally -// in router.Static(). -// if listDirectory == true, then it works the same as http.Dir() otherwise it returns -// a filesystem that prevents http.FileServer() to list the directory files. +// Readdir overrides the http.File default implementation and always returns nil. +func (n neutralizedReaddirFile) Readdir(_ int) ([]os.FileInfo, error) { + // this disables directory listing + return nil, nil +} + +// Dir returns an http.FileSystem that can be used by http.FileServer(). +// It is used internally in router.Static(). +// if listDirectory == true, then it works the same as http.Dir(), +// otherwise it returns a filesystem that prevents http.FileServer() to list the directory files. func Dir(root string, listDirectory bool) http.FileSystem { fs := http.Dir(root) + if listDirectory { return fs } - return &onlyFilesFS{fs} -} - -// Open conforms to http.Filesystem. -func (fs onlyFilesFS) Open(name string) (http.File, error) { - f, err := fs.fs.Open(name) - if err != nil { - return nil, err - } - return neuteredReaddirFile{f}, nil -} -// Readdir overrides the http.File default implementation. -func (f neuteredReaddirFile) Readdir(_ int) ([]os.FileInfo, error) { - // this disables directory listing - return nil, nil + return &OnlyFilesFS{FileSystem: fs} } diff --git a/fs_test.go b/fs_test.go new file mode 100644 index 0000000000..a1690cd928 --- /dev/null +++ b/fs_test.go @@ -0,0 +1,71 @@ +package gin + +import ( + "errors" + "net/http" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +type mockFileSystem struct { + open func(name string) (http.File, error) +} + +func (m *mockFileSystem) Open(name string) (http.File, error) { + return m.open(name) +} + +func TestOnlyFilesFS_Open(t *testing.T) { + var testFile *os.File + mockFS := &mockFileSystem{ + open: func(name string) (http.File, error) { + return testFile, nil + }, + } + fs := &OnlyFilesFS{FileSystem: mockFS} + + file, err := fs.Open("foo") + + assert.NoError(t, err) + assert.Equal(t, testFile, file.(neutralizedReaddirFile).File) +} + +func TestOnlyFilesFS_Open_err(t *testing.T) { + testError := errors.New("mock") + mockFS := &mockFileSystem{ + open: func(_ string) (http.File, error) { + return nil, testError + }, + } + fs := &OnlyFilesFS{FileSystem: mockFS} + + file, err := fs.Open("foo") + + assert.ErrorIs(t, err, testError) + assert.Nil(t, file) +} + +func Test_neuteredReaddirFile_Readdir(t *testing.T) { + n := neutralizedReaddirFile{} + + res, err := n.Readdir(0) + + assert.NoError(t, err) + assert.Nil(t, res) +} + +func TestDir_listDirectory(t *testing.T) { + testRoot := "foo" + fs := Dir(testRoot, true) + + assert.Equal(t, http.Dir(testRoot), fs) +} + +func TestDir(t *testing.T) { + testRoot := "foo" + fs := Dir(testRoot, false) + + assert.Equal(t, &OnlyFilesFS{FileSystem: http.Dir(testRoot)}, fs) +} diff --git a/routergroup.go b/routergroup.go index c833fe8fe3..b2540ec11e 100644 --- a/routergroup.go +++ b/routergroup.go @@ -218,7 +218,7 @@ func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileS fileServer := http.StripPrefix(absolutePath, http.FileServer(fs)) return func(c *Context) { - if _, noListing := fs.(*onlyFilesFS); noListing { + if _, noListing := fs.(*OnlyFilesFS); noListing { c.Writer.WriteHeader(http.StatusNotFound) } From 3ac729dc4a497d360a23b9d7e766c622b3c99f51 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Thu, 9 May 2024 09:17:06 +0800 Subject: [PATCH 310/346] feat(gin): support http3 using quic-go/quic-go (#3210) * experimental support http3 * remove go1.14 and go1.15 * update quic-go package path * only support go1.19+ * remove go19 support * update gomod * chore: refine CI configuration and dependencies - Remove dynamic Go versioning in favor of pinning to major version `1` - Update linter version from `v1.56.2` to `v1.58.1` in GitHub Actions workflow Signed-off-by: Bo-Yi Wu * chore: refactor CI workflow and improve tests - Update the golangci-lint-action version from `v5` to `v6` in the GitHub workflow configuration Signed-off-by: Bo-Yi Wu * chore: update dependencies and CI configurations - Update Go version requirement from `1.20` to `1.21` in `go.mod` Signed-off-by: Bo-Yi Wu * style: refactor codebase and update tests - Add an empty line in the import section of `gin.go` Signed-off-by: Bo-Yi Wu * chore: enhance code quality and consistency - Add `gin.go` to the list of files with specific linters in `.golangci.yml`, applying the `gci` linter. Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu Co-authored-by: Bo-Yi Wu --- .github/workflows/gin.yml | 7 ++--- .golangci.yml | 3 ++ gin.go | 52 +++++++++++++++++++++++------------ gin_integration_test.go | 16 +++++++++++ go.mod | 18 +++++++++--- go.sum | 58 ++++++++++++++++++++++++++++++++------- 6 files changed, 119 insertions(+), 35 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 4a9dbc9368..61210772fc 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -22,12 +22,11 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version-file: "go.mod" - check-latest: true + go-version: "^1" - name: Setup golangci-lint - uses: golangci/golangci-lint-action@v5 + uses: golangci/golangci-lint-action@v6 with: - version: v1.56.2 + version: v1.58.1 args: --verbose test: needs: lint diff --git a/.golangci.yml b/.golangci.yml index 4a72f7342f..5a65972a64 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -55,3 +55,6 @@ issues: - linters: - revive path: _test\.go + - path: gin.go + linters: + - gci diff --git a/gin.go b/gin.go index 03edbfff2f..57f8c2a3d9 100644 --- a/gin.go +++ b/gin.go @@ -17,6 +17,8 @@ import ( "github.com/gin-gonic/gin/internal/bytesconv" "github.com/gin-gonic/gin/render" + + "github.com/quic-go/quic-go/http3" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" ) @@ -383,23 +385,6 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo { return routes } -// Run attaches the router to a http.Server and starts listening and serving HTTP requests. -// It is a shortcut for http.ListenAndServe(addr, router) -// Note: this method will block the calling goroutine indefinitely unless an error happens. -func (engine *Engine) Run(addr ...string) (err error) { - defer func() { debugPrintError(err) }() - - if engine.isUnsafeTrustedProxies() { - debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + - "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") - } - - address := resolveAddress(addr) - debugPrint("Listening and serving HTTP on %s\n", address) - err = http.ListenAndServe(address, engine.Handler()) - return -} - func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) { if engine.trustedProxies == nil { return nil, nil @@ -503,6 +488,23 @@ func parseIP(ip string) net.IP { return parsedIP } +// Run attaches the router to a http.Server and starts listening and serving HTTP requests. +// It is a shortcut for http.ListenAndServe(addr, router) +// Note: this method will block the calling goroutine indefinitely unless an error happens. +func (engine *Engine) Run(addr ...string) (err error) { + defer func() { debugPrintError(err) }() + + if engine.isUnsafeTrustedProxies() { + debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") + } + + address := resolveAddress(addr) + debugPrint("Listening and serving HTTP on %s\n", address) + err = http.ListenAndServe(address, engine.Handler()) + return +} + // RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests. // It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) // Note: this method will block the calling goroutine indefinitely unless an error happens. @@ -564,6 +566,22 @@ func (engine *Engine) RunFd(fd int) (err error) { return } +// RunQUIC attaches the router to a http.Server and starts listening and serving QUIC requests. +// It is a shortcut for http3.ListenAndServeQUIC(addr, certFile, keyFile, router) +// Note: this method will block the calling goroutine indefinitely unless an error happens. +func (engine *Engine) RunQUIC(addr, certFile, keyFile string) (err error) { + debugPrint("Listening and serving QUIC on %s\n", addr) + defer func() { debugPrintError(err) }() + + if engine.isUnsafeTrustedProxies() { + debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + } + + err = http3.ListenAndServeQUIC(addr, certFile, keyFile, engine.Handler()) + return +} + // RunListener attaches the router to a http.Server and starts listening and serving HTTP requests // through the specified net.Listener func (engine *Engine) RunListener(listener net.Listener) (err error) { diff --git a/gin_integration_test.go b/gin_integration_test.go index 02b9622119..2125df925b 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -274,6 +274,22 @@ func TestBadUnixSocket(t *testing.T) { assert.Error(t, router.RunUnix("#/tmp/unix_unit_test")) } +func TestRunQUIC(t *testing.T) { + router := New() + go func() { + router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) + + assert.NoError(t, router.RunQUIC(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) + }() + + // have to wait for the goroutine to start and run the server + // otherwise the main thread will complete + time.Sleep(5 * time.Millisecond) + + assert.Error(t, router.RunQUIC(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) + testRequest(t, "https://localhost:8443/example") +} + func TestFileDescriptor(t *testing.T) { router := New() diff --git a/go.mod b/go.mod index 3e94e50585..6063f3bc99 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gin-gonic/gin -go 1.20 +go 1.21 require ( github.com/bytedance/sonic v1.11.6 @@ -10,6 +10,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.20 github.com/pelletier/go-toml/v2 v2.2.2 + github.com/quic-go/quic-go v0.43.1 github.com/stretchr/testify v1.9.0 github.com/ugorji/go/codec v1.2.12 golang.org/x/net v0.25.0 @@ -25,14 +26,23 @@ require ( github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/quic-go/qpack v0.4.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - golang.org/x/arch v0.8.0 // indirect + go.uber.org/mock v0.4.0 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect + golang.org/x/mod v0.11.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect + golang.org/x/tools v0.9.1 // indirect ) diff --git a/go.sum b/go.sum index ce905e701b..44af4cc163 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,14 @@ github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -13,41 +17,66 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ= +github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -59,24 +88,33 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 7e298066baab19316aa2ffc946f1bbc44a68a607 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 9 May 2024 13:45:03 +0800 Subject: [PATCH 311/346] build: update Gin minimum Go version to 1.21 (#3960) * build: update Gin minimum Go version to 1.21 - Update the minimum Go version requirement for Gin from `1.20` to `1.21` in both `debug.go` and `debug_test.go` - Modify the warning message to reflect the new minimum Go version requirement in `debug.go` - Adjust the test assertion to match the updated warning message in `debug_test.go` Signed-off-by: Bo-Yi Wu * docs: refine project documentation and CI configurations - Update supported Go versions for GitHub actions to `1.21` and `1.22` - Specify the required Go version as `1.21` or above in README - Change code block syntax to `sh` in installation and demo run instructions - Remove empty lines in README sections - Update project list formatting without changing the content Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu --- .github/workflows/gin.yml | 2 +- README.md | 25 ++++++++++--------------- debug.go | 4 ++-- debug_test.go | 2 +- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 61210772fc..947abf9c90 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -33,7 +33,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: ["1.20", "1.21", "1.22"] + go: ["1.21", "1.22"] test-tags: ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json", "-race"] include: diff --git a/README.md b/README.md index e007bf2fbb..04217d64ae 100644 --- a/README.md +++ b/README.md @@ -25,18 +25,17 @@ Gin is a web framework written in [Go](https://go.dev/). It features a martini-l - Rendering built-in - Extendable - ## Getting started ### Prerequisites -- **[Go](https://go.dev/)**: any one of the **three latest major** [releases](https://go.dev/doc/devel/release) (we test it with these). +The required version of [Go](https://go.dev/) language is [1.21](https://go.dev/doc/devel/release#go1.21.0) or above. ### Getting Gin With [Go module](https://github.com/golang/go/wiki/Modules) support, simply add the following import -``` +```sh import "github.com/gin-gonic/gin" ``` @@ -45,7 +44,7 @@ to your code, and then `go [build|run|test]` will automatically fetch the necess Otherwise, run the following Go command to install the `gin` package: ```sh -$ go get -u github.com/gin-gonic/gin +go get -u github.com/gin-gonic/gin ``` ### Running Gin @@ -74,7 +73,7 @@ func main() { And use the Go command to run the demo: -``` +```sh # run example.go and visit 0.0.0.0:8080/ping on browser $ go run example.go ``` @@ -89,7 +88,6 @@ Learn and practice more examples, please read the [Gin Quick Start](docs/doc.md) A number of ready-to-run examples demonstrating various use cases of Gin on the [Gin examples](https://github.com/gin-gonic/examples) repository. - ## Documentation See [API documentation and descriptions](https://godoc.org/github.com/gin-gonic/gin) for package. @@ -153,23 +151,20 @@ Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httpr - (3): Heap Memory (B/op), lower is better - (4): Average Allocations per Repetition (allocs/op), lower is better - ## Middlewares You can find many useful Gin middlewares at [gin-contrib](https://github.com/gin-contrib). - ## Users Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. -* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. -* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform. -* [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. -* [lura](https://github.com/luraproject/lura): Ultra performant API Gateway with middlewares. -* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. -* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. - +- [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. +- [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform. +- [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. +- [lura](https://github.com/luraproject/lura): Ultra performant API Gateway with middlewares. +- [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. +- [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. ## Contributing diff --git a/debug.go b/debug.go index ae346e9c38..62085c5dc5 100644 --- a/debug.go +++ b/debug.go @@ -13,7 +13,7 @@ import ( "sync/atomic" ) -const ginSupportMinGoVer = 18 +const ginSupportMinGoVer = 21 // IsDebugging returns true if the framework is running in debug mode. // Use SetMode(gin.ReleaseMode) to disable debug mode. @@ -78,7 +78,7 @@ func getMinVer(v string) (uint64, error) { func debugPrintWARNINGDefault() { if v, e := getMinVer(runtime.Version()); e == nil && v < ginSupportMinGoVer { - debugPrint(`[WARNING] Now Gin requires Go 1.20+. + debugPrint(`[WARNING] Now Gin requires Go 1.21+. `) } diff --git a/debug_test.go b/debug_test.go index e39097298a..1e576681ec 100644 --- a/debug_test.go +++ b/debug_test.go @@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { }) m, e := getMinVer(runtime.Version()) if e == nil && m < ginSupportMinGoVer { - assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.20+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) + assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.21+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } else { assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } From c677ccc40a60386565dd0d755efacb85d153feca Mon Sep 17 00:00:00 2001 From: thinkerou Date: Fri, 10 May 2024 07:27:42 +0800 Subject: [PATCH 312/346] fix(go): invalid Go toolchain version (#3961) --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 6063f3bc99..4937d2b7cf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gin-gonic/gin -go 1.21 +go 1.21.0 require ( github.com/bytedance/sonic v1.11.6 From 40131af1243ef90e026859bf8ff9c30a5a230351 Mon Sep 17 00:00:00 2001 From: Mobin Mohanan <47410557+tr1sm0s1n@users.noreply.github.com> Date: Mon, 13 May 2024 06:59:21 +0530 Subject: [PATCH 313/346] ci(Makefile): added help and descriptions to targets (#3964) --- Makefile | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index b58f24f32b..1a7de86b71 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | gr TESTTAGS ?= "" .PHONY: test +# Run tests to verify code functionality. test: echo "mode: count" > coverage.out for d in $(TESTFOLDER); do \ @@ -30,10 +31,12 @@ test: done .PHONY: fmt +# Ensure consistent code formatting. fmt: $(GOFMT) -w $(GOFILES) .PHONY: fmt-check +# format (check only). fmt-check: @diff=$$($(GOFMT) -d $(GOFILES)); \ if [ -n "$$diff" ]; then \ @@ -43,31 +46,36 @@ fmt-check: fi; .PHONY: vet +# Examine packages and report suspicious constructs if any. vet: $(GO) vet $(VETPACKAGES) .PHONY: lint +# Inspect source code for stylistic errors or potential bugs. lint: @hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ $(GO) get -u golang.org/x/lint/golint; \ fi for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done; -.PHONY: misspell-check -misspell-check: +.PHONY: misspell +# Correct commonly misspelled English words in source code. +misspell: @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ $(GO) get -u github.com/client9/misspell/cmd/misspell; \ fi - misspell -error $(GOFILES) + misspell -w $(GOFILES) -.PHONY: misspell -misspell: +.PHONY: misspell-check +# misspell (check only). +misspell-check: @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ $(GO) get -u github.com/client9/misspell/cmd/misspell; \ fi - misspell -w $(GOFILES) + misspell -error $(GOFILES) .PHONY: tools +# Install tools (golint and misspell). tools: @if [ $(GO_VERSION) -gt 15 ]; then \ $(GO) install golang.org/x/lint/golint@latest; \ @@ -76,3 +84,23 @@ tools: $(GO) install golang.org/x/lint/golint; \ $(GO) install github.com/client9/misspell/cmd/misspell; \ fi + +.PHONY: help +# Help. +help: + @echo '' + @echo 'Usage:' + @echo ' make [target]' + @echo '' + @echo 'Targets:' + @awk '/^[a-zA-Z\-\0-9]+:/ { \ + helpMessage = match(lastLine, /^# (.*)/); \ + if (helpMessage) { \ + helpCommand = substr($$1, 0, index($$1, ":")-1); \ + helpMessage = substr(lastLine, RSTART + 2, RLENGTH); \ + printf " - \033[36m%-20s\033[0m %s\n", helpCommand, helpMessage; \ + } \ + } \ + { lastLine = $$0 }' $(MAKEFILE_LIST) + +.DEFAULT_GOAL := help From 6ca8ddb1aed78d9ffaf984e5489111838242fedb Mon Sep 17 00:00:00 2001 From: guonaihong Date: Mon, 13 May 2024 11:11:56 +0800 Subject: [PATCH 314/346] feat(binding): add BindPlain (#3904) * add BindPlain * fix ci/cd error --- binding/binding.go | 1 + binding/binding_nomsgpack.go | 1 + binding/binding_test.go | 40 +++++++++++ binding/plain.go | 56 ++++++++++++++++ context.go | 17 ++++- context_test.go | 124 +++++++++++++++++++++++++++++++++++ 6 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 binding/plain.go diff --git a/binding/binding.go b/binding/binding.go index 94723879a7..702d0e82f6 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -84,6 +84,7 @@ var ( YAML BindingBody = yamlBinding{} Uri BindingUri = uriBinding{} Header Binding = headerBinding{} + Plain BindingBody = plainBinding{} TOML BindingBody = tomlBinding{} ) diff --git a/binding/binding_nomsgpack.go b/binding/binding_nomsgpack.go index 552a86b2d7..c8e6131012 100644 --- a/binding/binding_nomsgpack.go +++ b/binding/binding_nomsgpack.go @@ -81,6 +81,7 @@ var ( Uri = uriBinding{} Header = headerBinding{} TOML = tomlBinding{} + Plain = plainBinding{} ) // Default returns the appropriate Binding instance based on the HTTP method diff --git a/binding/binding_test.go b/binding/binding_test.go index feb8eed558..c59e5e939a 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -1342,6 +1342,46 @@ func (h hook) Read([]byte) (int, error) { return 0, errors.New("error") } +type failRead struct{} + +func (f *failRead) Read(b []byte) (n int, err error) { + return 0, errors.New("my fail") +} + +func (f *failRead) Close() error { + return nil +} + +func TestPlainBinding(t *testing.T) { + p := Plain + assert.Equal(t, "plain", p.Name()) + + var s string + req := requestWithBody("POST", "/", "test string") + assert.NoError(t, p.Bind(req, &s)) + assert.Equal(t, s, "test string") + + var bs []byte + req = requestWithBody("POST", "/", "test []byte") + assert.NoError(t, p.Bind(req, &bs)) + assert.Equal(t, bs, []byte("test []byte")) + + var i int + req = requestWithBody("POST", "/", "test fail") + assert.Error(t, p.Bind(req, &i)) + + req = requestWithBody("POST", "/", "") + req.Body = &failRead{} + assert.Error(t, p.Bind(req, &s)) + + req = requestWithBody("POST", "/", "") + assert.Nil(t, p.Bind(req, nil)) + + var ptr *string + req = requestWithBody("POST", "/", "") + assert.Nil(t, p.Bind(req, ptr)) +} + func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) { assert.Equal(t, name, b.Name()) diff --git a/binding/plain.go b/binding/plain.go new file mode 100644 index 0000000000..3b250bb07a --- /dev/null +++ b/binding/plain.go @@ -0,0 +1,56 @@ +package binding + +import ( + "fmt" + "io" + "net/http" + "reflect" + + "github.com/gin-gonic/gin/internal/bytesconv" +) + +type plainBinding struct{} + +func (plainBinding) Name() string { + return "plain" +} + +func (plainBinding) Bind(req *http.Request, obj interface{}) error { + all, err := io.ReadAll(req.Body) + if err != nil { + return err + } + + return decodePlain(all, obj) +} + +func (plainBinding) BindBody(body []byte, obj any) error { + return decodePlain(body, obj) +} + +func decodePlain(data []byte, obj any) error { + if obj == nil { + return nil + } + + v := reflect.ValueOf(obj) + + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return nil + } + v = v.Elem() + } + + if v.Kind() == reflect.String { + v.SetString(bytesconv.BytesToString(data)) + return nil + } + + if _, ok := v.Interface().([]byte); ok { + v.SetBytes(data) + return nil + } + + return fmt.Errorf("type (%T) unknown type", v) +} diff --git a/context.go b/context.go index 391adafed0..3525066752 100644 --- a/context.go +++ b/context.go @@ -614,7 +614,7 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error } defer src.Close() - if err = os.MkdirAll(filepath.Dir(dst), 0750); err != nil { + if err = os.MkdirAll(filepath.Dir(dst), 0o750); err != nil { return err } @@ -667,6 +667,11 @@ func (c *Context) BindTOML(obj any) error { return c.MustBindWith(obj, binding.TOML) } +// BindPlain is a shortcut for c.MustBindWith(obj, binding.Plain). +func (c *Context) BindPlain(obj any) error { + return c.MustBindWith(obj, binding.Plain) +} + // BindHeader is a shortcut for c.MustBindWith(obj, binding.Header). func (c *Context) BindHeader(obj any) error { return c.MustBindWith(obj, binding.Header) @@ -732,6 +737,11 @@ func (c *Context) ShouldBindTOML(obj any) error { return c.ShouldBindWith(obj, binding.TOML) } +// ShouldBindPlain is a shortcut for c.ShouldBindWith(obj, binding.Plain). +func (c *Context) ShouldBindPlain(obj any) error { + return c.ShouldBindWith(obj, binding.Plain) +} + // ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header). func (c *Context) ShouldBindHeader(obj any) error { return c.ShouldBindWith(obj, binding.Header) @@ -794,6 +804,11 @@ func (c *Context) ShouldBindBodyWithTOML(obj any) error { return c.ShouldBindBodyWith(obj, binding.TOML) } +// ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON). +func (c *Context) ShouldBindBodyWithPlain(obj any) error { + return c.ShouldBindBodyWith(obj, binding.Plain) +} + // ClientIP implements one best effort algorithm to return the real client IP. // It calls 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]). diff --git a/context_test.go b/context_test.go index ae34c6592e..36d6e34ac4 100644 --- a/context_test.go +++ b/context_test.go @@ -1670,6 +1670,31 @@ func TestContextBindWithXML(t *testing.T) { assert.Equal(t, 0, w.Body.Len()) } +func TestContextBindPlain(t *testing.T) { + + // string + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`)) + c.Request.Header.Add("Content-Type", MIMEPlain) + + var s string + + assert.NoError(t, c.BindPlain(&s)) + assert.Equal(t, "test string", s) + assert.Equal(t, 0, w.Body.Len()) + + // []byte + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`)) + c.Request.Header.Add("Content-Type", MIMEPlain) + + var bs []byte + + assert.NoError(t, c.BindPlain(&bs)) + assert.Equal(t, []byte("test []byte"), bs) + assert.Equal(t, 0, w.Body.Len()) +} + func TestContextBindHeader(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) @@ -1816,6 +1841,31 @@ func TestContextShouldBindWithXML(t *testing.T) { assert.Equal(t, 0, w.Body.Len()) } +func TestContextShouldBindPlain(t *testing.T) { + // string + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`)) + c.Request.Header.Add("Content-Type", MIMEPlain) + + var s string + + assert.NoError(t, c.ShouldBindPlain(&s)) + assert.Equal(t, "test string", s) + assert.Equal(t, 0, w.Body.Len()) + // []byte + + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`)) + c.Request.Header.Add("Content-Type", MIMEPlain) + + var bs []byte + + assert.NoError(t, c.ShouldBindPlain(&bs)) + assert.Equal(t, []byte("test []byte"), bs) + assert.Equal(t, 0, w.Body.Len()) + +} + func TestContextShouldBindHeader(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) @@ -2247,6 +2297,80 @@ func TestContextShouldBindBodyWithTOML(t *testing.T) { } } +func TestContextShouldBindBodyWithPlain(t *testing.T) { + for _, tt := range []struct { + name string + bindingBody binding.BindingBody + body string + }{ + { + name: " JSON & JSON-BODY ", + bindingBody: binding.JSON, + body: `{"foo":"FOO"}`, + }, + { + name: " JSON & XML-BODY ", + bindingBody: binding.XML, + body: ` + +FOO +`, + }, + { + name: " JSON & YAML-BODY ", + bindingBody: binding.YAML, + body: `foo: FOO`, + }, + { + name: " JSON & TOM-BODY ", + bindingBody: binding.TOML, + body: `foo=FOO`, + }, + { + name: " JSON & Plain-BODY ", + bindingBody: binding.Plain, + body: `foo=FOO`, + }, + } { + t.Logf("testing: %s", tt.name) + + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + + type typeJSON struct { + Foo string `json:"foo" binding:"required"` + } + objJSON := typeJSON{} + + if tt.bindingBody == binding.Plain { + body := "" + assert.NoError(t, c.ShouldBindBodyWithPlain(&body)) + assert.Equal(t, body, "foo=FOO") + } + + if tt.bindingBody == binding.JSON { + assert.NoError(t, c.ShouldBindBodyWithJSON(&objJSON)) + assert.Equal(t, typeJSON{"FOO"}, objJSON) + } + + if tt.bindingBody == binding.XML { + assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + assert.Equal(t, typeJSON{}, objJSON) + } + + if tt.bindingBody == binding.YAML { + assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + assert.Equal(t, typeJSON{}, objJSON) + } + + if tt.bindingBody == binding.TOML { + assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + assert.Equal(t, typeJSON{}, objJSON) + } + } +} func TestContextGolangContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) From a569ed8f26a13b10c02920de103eba79c7102cd7 Mon Sep 17 00:00:00 2001 From: crunchyfrog <49813441+truecrunchyfrog@users.noreply.github.com> Date: Mon, 13 May 2024 05:12:55 +0200 Subject: [PATCH 315/346] docs(readme): fix language and moved link (#3962) * Update README.md * more fixes & fix moved link --- README.md | 62 +++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 04217d64ae..a595656ca7 100644 --- a/README.md +++ b/README.md @@ -11,37 +11,36 @@ [![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases) [![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/gin-gonic/gin)](https://www.tickgit.com/browse?repo=github.com/gin-gonic/gin) -Gin is a web framework written in [Go](https://go.dev/). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. +Gin is a web framework written in [Go](https://go.dev/). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). +If you need performance and good productivity, you will love Gin. -**The key features of Gin are:** +**Gin's key features are:** - Zero allocation router -- Fast +- Speed - Middleware support - Crash-free - JSON validation -- Routes grouping +- Route grouping - Error management -- Rendering built-in -- Extendable +- Built-in rendering +- Extensible ## Getting started ### Prerequisites -The required version of [Go](https://go.dev/) language is [1.21](https://go.dev/doc/devel/release#go1.21.0) or above. +Gin requires [Go](https://go.dev/) version [1.21](https://go.dev/doc/devel/release#go1.21.0) or above. ### Getting Gin -With [Go module](https://github.com/golang/go/wiki/Modules) support, simply add the following import +With [Go's module support](https://go.dev/wiki/Modules#how-to-use-modules), `go [build|run|test]` automatically fetches the necessary dependencies when you add the import in your code: ```sh import "github.com/gin-gonic/gin" ``` -to your code, and then `go [build|run|test]` will automatically fetch the necessary dependencies. - -Otherwise, run the following Go command to install the `gin` package: +Alternatively, use `go get`: ```sh go get -u github.com/gin-gonic/gin @@ -49,7 +48,7 @@ go get -u github.com/gin-gonic/gin ### Running Gin -First you need to import Gin package for using Gin, one simplest example likes the follow `example.go`: +A basic example: ```go package main @@ -71,28 +70,29 @@ func main() { } ``` -And use the Go command to run the demo: +To run the code, use the `go run` command, like: ```sh -# run example.go and visit 0.0.0.0:8080/ping on browser $ go run example.go ``` -### Learn more examples +Then visit [`0.0.0.0:8080/ping`](http://0.0.0.0:8080/ping) in your browser to see the response! + +### See more examples #### Quick Start -Learn and practice more examples, please read the [Gin Quick Start](docs/doc.md) which includes API examples and builds tag. +Learn and practice with the [Gin Quick Start](docs/doc.md), which includes API examples and builds tag. #### Examples -A number of ready-to-run examples demonstrating various use cases of Gin on the [Gin examples](https://github.com/gin-gonic/examples) repository. +A number of ready-to-run examples demonstrating various use cases of Gin are available in the [Gin examples](https://github.com/gin-gonic/examples) repository. ## Documentation -See [API documentation and descriptions](https://godoc.org/github.com/gin-gonic/gin) for package. +See the [API documentation on godoc.org](https://godoc.org/github.com/gin-gonic/gin). -All documentation is available on the Gin website. +The documentation is also available on [gin-gonic.com](https://gin-gonic.com) in several languages: - [English](https://gin-gonic.com/docs/) - [简体中文](https://gin-gonic.com/zh-cn/docs/) @@ -103,15 +103,13 @@ All documentation is available on the Gin website. - [Turkish](https://gin-gonic.com/tr/docs/) - [Persian](https://gin-gonic.com/fa/docs/) -### Articles about Gin - -A curated list of awesome Gin framework. +### Articles - [Tutorial: Developing a RESTful API with Go and Gin](https://go.dev/doc/tutorial/web-service-gin) ## Benchmarks -Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter), [see all benchmarks details](/BENCHMARKS.md). +Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter), [see all benchmarks](/BENCHMARKS.md). | Benchmark name | (1) | (2) | (3) | (4) | | ------------------------------ | ---------:| ---------------:| ------------:| ---------------:| @@ -151,23 +149,23 @@ Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httpr - (3): Heap Memory (B/op), lower is better - (4): Average Allocations per Repetition (allocs/op), lower is better -## Middlewares +## Middleware You can find many useful Gin middlewares at [gin-contrib](https://github.com/gin-contrib). -## Users +## Uses -Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. +Here are some awesome projects that are using the [Gin](https://github.com/gin-gonic/gin) web framework. -- [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. -- [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform. -- [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. -- [lura](https://github.com/luraproject/lura): Ultra performant API Gateway with middlewares. -- [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. +- [gorush](https://github.com/appleboy/gorush): A push notification server. +- [fnproject](https://github.com/fnproject/fn): A container native, cloud agnostic serverless platform. +- [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Google TensorFlow. +- [lura](https://github.com/luraproject/lura): Ultra performant API Gateway with middleware. +- [picfit](https://github.com/thoas/picfit): An image resizing server. - [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. ## Contributing Gin is the work of hundreds of contributors. We appreciate your help! -Please see [CONTRIBUTING](CONTRIBUTING.md) for details on submitting patches and the contribution workflow. +Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on submitting patches and the contribution workflow. From 3f5b0afa2ac85ea79638ca08f4140ce64b8246e5 Mon Sep 17 00:00:00 2001 From: Name <1911860538@qq.com> Date: Mon, 13 May 2024 13:32:46 +0800 Subject: [PATCH 316/346] refactor(slice): simplify SliceValidationError Error method (#3910) * Simplify SliceValidationError Error method * Replace fmt.Fprintf with b.WriteString --------- Co-authored-by: huangzw Co-authored-by: 1911860538 --- binding/default_validator.go | 27 +++++++++------------ binding/default_validator_benchmark_test.go | 12 ++++++--- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/binding/default_validator.go b/binding/default_validator.go index ac43d7cc5e..44b7a2ac2a 100644 --- a/binding/default_validator.go +++ b/binding/default_validator.go @@ -5,8 +5,8 @@ package binding import ( - "fmt" "reflect" + "strconv" "strings" "sync" @@ -22,25 +22,20 @@ type SliceValidationError []error // 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: + if len(err) == 0 { return "" - default: - var b strings.Builder - if err[0] != nil { - fmt.Fprintf(&b, "[%d]: %s", 0, err[0].Error()) - } - if n > 1 { - for i := 1; i < n; i++ { - if err[i] != nil { - b.WriteString("\n") - fmt.Fprintf(&b, "[%d]: %s", i, err[i].Error()) - } + } + + var b strings.Builder + for i := 0; i < len(err); i++ { + if err[i] != nil { + if b.Len() > 0 { + b.WriteString("\n") } + b.WriteString("[" + strconv.Itoa(i) + "]: " + err[i].Error()) } - return b.String() } + return b.String() } var _ StructValidator = (*defaultValidator)(nil) diff --git a/binding/default_validator_benchmark_test.go b/binding/default_validator_benchmark_test.go index 9292e2aaf1..44547412c1 100644 --- a/binding/default_validator_benchmark_test.go +++ b/binding/default_validator_benchmark_test.go @@ -12,11 +12,15 @@ import ( func BenchmarkSliceValidationError(b *testing.B) { const size int = 100 + e := make(SliceValidationError, size) + for j := 0; j < size; j++ { + e[j] = errors.New(strconv.Itoa(j)) + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { - e := make(SliceValidationError, size) - for j := 0; j < size; j++ { - e[j] = errors.New(strconv.Itoa(j)) - } if len(e.Error()) == 0 { b.Errorf("error") } From 36b0dede4b8c4a67d92c4107cebc5a068364321d Mon Sep 17 00:00:00 2001 From: 51pwn <18223385+hktalent@users.noreply.github.com> Date: Mon, 13 May 2024 14:55:41 +0800 Subject: [PATCH 317/346] fix(context): check handler is nil (#3413) * fixed #3404 2022-11-23 * up 2022-11-23 * refactor: refactor context handling and nil checks - Refactor nil checks to improve readability in `context.go` - Modify the control flow in `HandlerNames` and `Next` methods to continue on nil values before appending or invoking handlers in `context.go` Signed-off-by: Bo-Yi Wu * test: refactor context_test.go for clarity and efficiency - Insert a `nil` value into the `HandlersChain` array in `context_test.go` - Remove empty test functions in `context_test.go` Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu Co-authored-by: Bo-Yi Wu --- context.go | 6 ++++++ context_test.go | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index 3525066752..cd2645a93e 100644 --- a/context.go +++ b/context.go @@ -152,6 +152,9 @@ func (c *Context) HandlerName() string { func (c *Context) HandlerNames() []string { hn := make([]string, 0, len(c.handlers)) for _, val := range c.handlers { + if val == nil { + continue + } hn = append(hn, nameOfFunction(val)) } return hn @@ -182,6 +185,9 @@ func (c *Context) FullPath() string { func (c *Context) Next() { c.index++ for c.index < int8(len(c.handlers)) { + if c.handlers[c.index] == nil { + continue + } c.handlers[c.index](c) c.index++ } diff --git a/context_test.go b/context_test.go index 36d6e34ac4..517f73e2f6 100644 --- a/context_test.go +++ b/context_test.go @@ -362,7 +362,7 @@ func TestContextHandlerName(t *testing.T) { func TestContextHandlerNames(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.handlers = HandlersChain{func(c *Context) {}, handlerNameTest, func(c *Context) {}, handlerNameTest2} + c.handlers = HandlersChain{func(c *Context) {}, nil, handlerNameTest, func(c *Context) {}, handlerNameTest2} names := c.HandlerNames() @@ -1671,7 +1671,6 @@ func TestContextBindWithXML(t *testing.T) { } func TestContextBindPlain(t *testing.T) { - // string w := httptest.NewRecorder() c, _ := CreateTestContext(w) @@ -1863,7 +1862,6 @@ func TestContextShouldBindPlain(t *testing.T) { assert.NoError(t, c.ShouldBindPlain(&bs)) assert.Equal(t, []byte("test []byte"), bs) assert.Equal(t, 0, w.Body.Len()) - } func TestContextShouldBindHeader(t *testing.T) { @@ -2371,6 +2369,7 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) { } } } + func TestContextGolangContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) From 4f339e6a35b163d31b30916b37f4176d385f41bd Mon Sep 17 00:00:00 2001 From: RedCrazyGhost <49381700+RedCrazyGhost@users.noreply.github.com> Date: Tue, 14 May 2024 10:25:54 +0800 Subject: [PATCH 318/346] fix(context): YAML judgment logic in Negotiate (#3966) --- context.go | 3 ++- context_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/context.go b/context.go index cd2645a93e..d2d5497e63 100644 --- a/context.go +++ b/context.go @@ -34,6 +34,7 @@ const ( MIMEPOSTForm = binding.MIMEPOSTForm MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm MIMEYAML = binding.MIMEYAML + MIMEYAML2 = binding.MIMEYAML2 MIMETOML = binding.MIMETOML ) @@ -1182,7 +1183,7 @@ func (c *Context) Negotiate(code int, config Negotiate) { data := chooseData(config.XMLData, config.Data) c.XML(code, data) - case binding.MIMEYAML: + case binding.MIMEYAML, binding.MIMEYAML2: data := chooseData(config.YAMLData, config.Data) c.YAML(code, data) diff --git a/context_test.go b/context_test.go index 517f73e2f6..b700389ae2 100644 --- a/context_test.go +++ b/context_test.go @@ -1196,7 +1196,7 @@ func TestContextNegotiationWithJSON(t *testing.T) { c.Request, _ = http.NewRequest("POST", "", nil) c.Negotiate(http.StatusOK, Negotiate{ - Offered: []string{MIMEJSON, MIMEXML, MIMEYAML}, + Offered: []string{MIMEJSON, MIMEXML, MIMEYAML, MIMEYAML2}, Data: H{"foo": "bar"}, }) @@ -1211,7 +1211,7 @@ func TestContextNegotiationWithXML(t *testing.T) { c.Request, _ = http.NewRequest("POST", "", nil) c.Negotiate(http.StatusOK, Negotiate{ - Offered: []string{MIMEXML, MIMEJSON, MIMEYAML}, + Offered: []string{MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2}, Data: H{"foo": "bar"}, }) @@ -1226,7 +1226,7 @@ func TestContextNegotiationWithYAML(t *testing.T) { c.Request, _ = http.NewRequest("POST", "", nil) c.Negotiate(http.StatusOK, Negotiate{ - Offered: []string{MIMEYAML, MIMEXML, MIMEJSON, MIMETOML}, + Offered: []string{MIMEYAML, MIMEXML, MIMEJSON, MIMETOML, MIMEYAML2}, Data: H{"foo": "bar"}, }) @@ -1241,7 +1241,7 @@ func TestContextNegotiationWithTOML(t *testing.T) { c.Request, _ = http.NewRequest("POST", "", nil) c.Negotiate(http.StatusOK, Negotiate{ - Offered: []string{MIMETOML, MIMEXML, MIMEJSON, MIMEYAML}, + Offered: []string{MIMETOML, MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2}, Data: H{"foo": "bar"}, }) From e0d46ded6cb6974d55a255ab122d1aa6ca0cd60e Mon Sep 17 00:00:00 2001 From: Adriano Sela Aviles Date: Sat, 18 May 2024 19:48:07 -0700 Subject: [PATCH 319/346] fix(context): verify URL is Non-nil in initQueryCache() (#3969) --- context.go | 2 +- context_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/context.go b/context.go index d2d5497e63..baa4b0f9c9 100644 --- a/context.go +++ b/context.go @@ -475,7 +475,7 @@ func (c *Context) QueryArray(key string) (values []string) { func (c *Context) initQueryCache() { if c.queryCache == nil { - if c.Request != nil { + if c.Request != nil && c.Request.URL != nil { c.queryCache = c.Request.URL.Query() } else { c.queryCache = url.Values{} diff --git a/context_test.go b/context_test.go index b700389ae2..8bbf270086 100644 --- a/context_test.go +++ b/context_test.go @@ -423,6 +423,49 @@ func TestContextQuery(t *testing.T) { assert.Empty(t, c.PostForm("foo")) } +func TestContextInitQueryCache(t *testing.T) { + validURL, err := url.Parse("https://github.com/gin-gonic/gin/pull/3969?key=value&otherkey=othervalue") + assert.Nil(t, err) + + tests := []struct { + testName string + testContext *Context + expectedQueryCache url.Values + }{ + { + testName: "queryCache should remain unchanged if already not nil", + testContext: &Context{ + queryCache: url.Values{"a": []string{"b"}}, + Request: &http.Request{URL: validURL}, // valid request for evidence that values weren't extracted + }, + expectedQueryCache: url.Values{"a": []string{"b"}}, + }, + { + testName: "queryCache should be empty when Request is nil", + testContext: &Context{Request: nil}, // explicit nil for readability + expectedQueryCache: url.Values{}, + }, + { + testName: "queryCache should be empty when Request.URL is nil", + testContext: &Context{Request: &http.Request{URL: nil}}, // explicit nil for readability + expectedQueryCache: url.Values{}, + }, + { + testName: "queryCache should be populated when it not yet populated and Request + Request.URL are non nil", + testContext: &Context{Request: &http.Request{URL: validURL}}, // explicit nil for readability + expectedQueryCache: url.Values{"key": []string{"value"}, "otherkey": []string{"othervalue"}}, + }, + } + + for _, test := range tests { + t.Run(test.testName, func(t *testing.T) { + test.testContext.initQueryCache() + assert.Equal(t, test.expectedQueryCache, test.testContext.queryCache) + }) + } + +} + func TestContextDefaultQueryOnEmptyRequest(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) // here c.Request == nil assert.NotPanics(t, func() { From 24d67647cb9b4e0bbdcdec7f0c2086e8004e1572 Mon Sep 17 00:00:00 2001 From: bruceNu1l <144002160+bruceNu1l@users.noreply.github.com> Date: Thu, 23 May 2024 10:16:11 +0800 Subject: [PATCH 320/346] feat(form): add custom string slice for form tag unmarshal (#3970) (#3971) Co-authored-by: Bruce Lee --- binding/form_mapping.go | 11 +++++ binding/form_mapping_test.go | 87 ++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 108606fa56..33389b2889 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -193,14 +193,25 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][ if !ok { vs = []string{opt.defaultValue} } + + if ok, err = trySetCustom(vs[0], value); ok { + return ok, err + } + return true, setSlice(vs, value, field) case reflect.Array: if !ok { vs = []string{opt.defaultValue} } + + if ok, err = trySetCustom(vs[0], value); ok { + return ok, err + } + if len(vs) != value.Len() { return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String()) } + return true, setArray(vs, value, field) default: var val string diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index ed01a08641..afd51f9d88 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -5,6 +5,7 @@ package binding import ( + "encoding/hex" "fmt" "mime/multipart" "reflect" @@ -422,3 +423,89 @@ func TestMappingCustomPointerStructTypeWithURITag(t *testing.T) { assert.EqualValues(t, "/foo", s.FileData.Path) assert.EqualValues(t, "happiness", s.FileData.Name) } + +type customPath []string + +func (p *customPath) UnmarshalParam(param string) error { + elems := strings.Split(param, "/") + n := len(elems) + if n < 2 { + return fmt.Errorf("invalid format") + } + + *p = elems + return nil +} + +func TestMappingCustomSliceUri(t *testing.T) { + var s struct { + FileData customPath `uri:"path"` + } + err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "uri") + assert.NoError(t, err) + + assert.EqualValues(t, "bar", s.FileData[0]) + assert.EqualValues(t, "foo", s.FileData[1]) +} + +func TestMappingCustomSliceForm(t *testing.T) { + var s struct { + FileData customPath `form:"path"` + } + err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "form") + assert.NoError(t, err) + + assert.EqualValues(t, "bar", s.FileData[0]) + assert.EqualValues(t, "foo", s.FileData[1]) +} + +type objectID [12]byte + +func (o *objectID) UnmarshalParam(param string) error { + oid, err := convertTo(param) + if err != nil { + return err + } + + *o = oid + return nil +} + +func convertTo(s string) (objectID, error) { + var nilObjectID objectID + if len(s) != 24 { + return nilObjectID, fmt.Errorf("invalid format") + } + + var oid [12]byte + _, err := hex.Decode(oid[:], []byte(s)) + if err != nil { + return nilObjectID, err + } + + return oid, nil +} + +func TestMappingCustomArrayUri(t *testing.T) { + var s struct { + FileData objectID `uri:"id"` + } + val := `664a062ac74a8ad104e0e80f` + err := mappingByPtr(&s, formSource{"id": {val}}, "uri") + assert.NoError(t, err) + + expected, _ := convertTo(val) + assert.EqualValues(t, expected, s.FileData) +} + +func TestMappingCustomArrayForm(t *testing.T) { + var s struct { + FileData objectID `form:"id"` + } + val := `664a062ac74a8ad104e0e80f` + err := mappingByPtr(&s, formSource{"id": {val}}, "form") + assert.NoError(t, err) + + expected, _ := convertTo(val) + assert.EqualValues(t, expected, s.FileData) +} From 334160bab772f6f93767b870f9d07c176cd4aa2b Mon Sep 17 00:00:00 2001 From: Endless Paradox Date: Fri, 24 May 2024 14:55:25 +0800 Subject: [PATCH 321/346] chore(tree): replace the self-defined 'min' to official one (#3975) --- tree.go | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/tree.go b/tree.go index 878023d1cf..ce0f065cdd 100644 --- a/tree.go +++ b/tree.go @@ -65,17 +65,10 @@ func (trees methodTrees) get(method string) *node { return nil } -func min(a, b int) int { - if a <= b { - return a - } - return b -} - func longestCommonPrefix(a, b string) int { i := 0 - max := min(len(a), len(b)) - for i < max && a[i] == b[i] { + max_ := min(len(a), len(b)) + for i < max_ && a[i] == b[i] { i++ } return i @@ -205,7 +198,7 @@ walk: } // Check if a child with the next path byte exists - for i, max := 0, len(n.indices); i < max; i++ { + for i, max_ := 0, len(n.indices); i < max_; i++ { if c == n.indices[i] { parentFullPathIndex += len(n.path) i = n.incrementChildPrio(i) @@ -770,7 +763,7 @@ walk: // Outer loop for walking the tree // Runes are up to 4 byte long, // -4 would definitely be another rune. var off int - for max := min(npLen, 3); off < max; off++ { + for max_ := min(npLen, 3); off < max_; off++ { if i := npLen - off; utf8.RuneStart(oldPath[i]) { // read rune from cached path rv, _ = utf8.DecodeRuneInString(oldPath[i:]) From 4621b7ac982335d9a74432e182dd2bfc6d841431 Mon Sep 17 00:00:00 2001 From: wssccc Date: Sat, 1 Jun 2024 13:44:57 +0800 Subject: [PATCH 322/346] feat(router): add literal colon support (#1432) (#2857) --- gin.go | 25 ++++++++++++++++++++++++- gin_integration_test.go | 25 +++++++++++++++++++++++++ tree.go | 12 ++++++++++++ tree_test.go | 22 ++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 57f8c2a3d9..5ba1cf6368 100644 --- a/gin.go +++ b/gin.go @@ -24,6 +24,9 @@ import ( ) const defaultMultipartMemory = 32 << 20 // 32 MB +const escapedColon = "\\:" +const colon = ":" +const backslash = "\\" var ( default404Body = []byte("404 page not found") @@ -474,6 +477,26 @@ func (engine *Engine) validateHeader(header string) (clientIP string, valid bool return "", false } +// updateRouteTree do update to the route tree recursively +func updateRouteTree(n *node) { + n.path = strings.ReplaceAll(n.path, escapedColon, colon) + n.fullPath = strings.ReplaceAll(n.fullPath, escapedColon, colon) + n.indices = strings.ReplaceAll(n.indices, backslash, colon) + if n.children == nil { + return + } + for _, child := range n.children { + updateRouteTree(child) + } +} + +// updateRouteTrees do update to the route trees +func (engine *Engine) updateRouteTrees() { + for _, tree := range engine.trees { + updateRouteTree(tree.root) + } +} + // 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 { @@ -498,7 +521,7 @@ func (engine *Engine) Run(addr ...string) (err error) { debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") } - + engine.updateRouteTrees() address := resolveAddress(addr) debugPrint("Listening and serving HTTP on %s\n", address) err = http.ListenAndServe(address, engine.Handler()) diff --git a/gin_integration_test.go b/gin_integration_test.go index 2125df925b..539827120e 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -577,3 +577,28 @@ func TestTreeRunDynamicRouting(t *testing.T) { func isWindows() bool { return runtime.GOOS == "windows" } + +func TestEscapedColon(t *testing.T) { + router := New() + f := func(u string) { + router.GET(u, func(c *Context) { c.String(http.StatusOK, u) }) + } + f("/r/r\\:r") + f("/r/r:r") + f("/r/r/:r") + f("/r/r/\\:r") + f("/r/r/r\\:r") + assert.Panics(t, func() { + f("\\foo:") + }) + + router.updateRouteTrees() + ts := httptest.NewServer(router) + defer ts.Close() + + testRequest(t, ts.URL+"/r/r123", "", "/r/r:r") + testRequest(t, ts.URL+"/r/r:r", "", "/r/r\\:r") + testRequest(t, ts.URL+"/r/r/r123", "", "/r/r/:r") + testRequest(t, ts.URL+"/r/r/:r", "", "/r/r/\\:r") + testRequest(t, ts.URL+"/r/r/r:r", "", "/r/r/r\\:r") +} diff --git a/tree.go b/tree.go index ce0f065cdd..b0a5f98222 100644 --- a/tree.go +++ b/tree.go @@ -262,7 +262,19 @@ walk: // Returns -1 as index, if no wildcard was found. func findWildcard(path string) (wildcard string, i int, valid bool) { // Find start + escapeColon := false for start, c := range []byte(path) { + if escapeColon { + escapeColon = false + if c == ':' { + continue + } + panic("invalid escape string in path '" + path + "'") + } + if c == '\\' { + escapeColon = true + continue + } // A wildcard starts with ':' (param) or '*' (catch-all) if c != ':' && c != '*' { continue diff --git a/tree_test.go b/tree_test.go index c9b03130b6..3aa3a59444 100644 --- a/tree_test.go +++ b/tree_test.go @@ -192,6 +192,7 @@ func TestTreeWildcard(t *testing.T) { "/get/abc/123abg/:param", "/get/abc/123abf/:param", "/get/abc/123abfff/:param", + "/get/abc/escaped_colon/test\\:param", } for _, route := range routes { tree.addRoute(route, fakeHandler(route)) @@ -315,6 +316,7 @@ func TestTreeWildcard(t *testing.T) { {"/get/abc/123abg/test", false, "/get/abc/123abg/:param", Params{Param{Key: "param", Value: "test"}}}, {"/get/abc/123abf/testss", false, "/get/abc/123abf/:param", Params{Param{Key: "param", Value: "testss"}}}, {"/get/abc/123abfff/te", false, "/get/abc/123abfff/:param", Params{Param{Key: "param", Value: "te"}}}, + {"/get/abc/escaped_colon/test\\:param", false, "/get/abc/escaped_colon/test\\:param", nil}, }) checkPriorities(t, tree) @@ -419,6 +421,9 @@ func TestTreeWildcardConflict(t *testing.T) { {"/id/:id", false}, {"/static/*file", false}, {"/static/", true}, + {"/escape/test\\:d1", false}, + {"/escape/test\\:d2", false}, + {"/escape/test:param", false}, } testRoutes(t, routes) } @@ -971,3 +976,20 @@ func TestTreeWildcardConflictEx(t *testing.T) { } } } + +func TestTreeInvalidEscape(t *testing.T) { + routes := map[string]bool{ + "/r1/r": true, + "/r2/:r": true, + "/r3/\\:r": true, + } + tree := &node{} + for route, valid := range routes { + recv := catchPanic(func() { + tree.addRoute(route, fakeHandler(route)) + }) + if recv == nil != valid { + t.Fatalf("%s should be %t but got %v", route, valid, recv) + } + } +} From 64ead9e6bd924d431f4dd612349bc5e13300e6fc Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Thu, 6 Jun 2024 17:10:03 +0800 Subject: [PATCH 323/346] docs(readme): replace godoc with pkg (#3985) * Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a595656ca7..faeb4952aa 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Build Status](https://github.com/gin-gonic/gin/workflows/Run%20Tests/badge.svg?branch=master)](https://github.com/gin-gonic/gin/actions?query=branch%3Amaster) [![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin) [![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin) -[![GoDoc](https://pkg.go.dev/badge/github.com/gin-gonic/gin?status.svg)](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc) +[![Go Reference](https://pkg.go.dev/badge/github.com/gin-gonic/gin?status.svg)](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc) [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge) [![Open Source Helpers](https://www.codetriage.com/gin-gonic/gin/badges/users.svg)](https://www.codetriage.com/gin-gonic/gin) [![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases) @@ -90,7 +90,7 @@ A number of ready-to-run examples demonstrating various use cases of Gin are ava ## Documentation -See the [API documentation on godoc.org](https://godoc.org/github.com/gin-gonic/gin). +See the [API documentation on go.dev](https://pkg.go.dev/github.com/gin-gonic/gin). The documentation is also available on [gin-gonic.com](https://gin-gonic.com) in several languages: From 9c081de9cdd1948f521d47d170d18cbc2981c33a Mon Sep 17 00:00:00 2001 From: demouth <1133178+demouth@users.noreply.github.com> Date: Sun, 16 Jun 2024 01:28:08 +0900 Subject: [PATCH 324/346] docs: fix typo in Gin Quick Start (#3997) --- docs/doc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/doc.md b/docs/doc.md index 177c4471e8..5136640929 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -524,7 +524,7 @@ func main() { return c.Writer.Status() < http.StatusInternalServerError } - engine.Use(gin.LoggerWithConfig(loggerConfig)) + router.Use(gin.LoggerWithConfig(loggerConfig)) router.Use(gin.Recovery()) // skipped From 626d55b0c02937645c21774cacc021713de88604 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Symoneaux Date: Sat, 22 Jun 2024 16:19:04 +0200 Subject: [PATCH 325/346] fix(gin): Do not panic when handling method not allowed on empty tree (#4003) Signed-off-by: Pierre-Henri Symoneaux --- gin.go | 2 +- gin_test.go | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 5ba1cf6368..48cc15c985 100644 --- a/gin.go +++ b/gin.go @@ -687,7 +687,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) { break } - if engine.HandleMethodNotAllowed { + if engine.HandleMethodNotAllowed && len(t) > 0 { // According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response // containing a list of the target resource's currently supported methods. allowed := make([]string, 0, len(t)-1) diff --git a/gin_test.go b/gin_test.go index e68f1ce8fd..db70a8c5c7 100644 --- a/gin_test.go +++ b/gin_test.go @@ -754,3 +754,14 @@ func TestCustomUnmarshalStruct(t *testing.T) { assert.Equal(t, 200, w.Code) assert.Equal(t, `"2000/01/01"`, w.Body.String()) } + +// Test the fix for https://github.com/gin-gonic/gin/issues/4002 +func TestMethodNotAllowedNoRoute(t *testing.T) { + g := New() + g.HandleMethodNotAllowed = true + + req := httptest.NewRequest("GET", "/", nil) + resp := httptest.NewRecorder() + assert.NotPanics(t, func() { g.ServeHTTP(resp, req) }) + assert.Equal(t, http.StatusNotFound, resp.Code) +} From 5f55c6a711376c77834bc6b25d35c8985de1d311 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Sun, 14 Jul 2024 14:33:08 +0200 Subject: [PATCH 326/346] ci(lint): enable testifylint linter (#4010) Signed-off-by: Matthieu MOREL --- .golangci.yml | 3 + binding/binding_msgpack_test.go | 7 +- binding/binding_test.go | 259 +++++++++++++------------ binding/form_mapping_test.go | 79 ++++---- binding/multipart_form_mapping_test.go | 21 +- binding/validate_test.go | 39 ++-- context_test.go | 239 +++++++++++------------ debug_test.go | 9 +- errors_test.go | 5 +- fs_test.go | 7 +- gin_integration_test.go | 89 ++++----- gin_test.go | 27 +-- githubapi_test.go | 15 +- logger_test.go | 12 +- path_test.go | 2 +- render/render_msgpack_test.go | 5 +- render/render_test.go | 75 +++---- response_writer_test.go | 9 +- routes_test.go | 7 +- utils_test.go | 4 +- 20 files changed, 461 insertions(+), 452 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 5a65972a64..8d58c989be 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -17,6 +17,7 @@ linters: - nilerr - nolintlint - revive + - testifylint - wastedassign linters-settings: @@ -33,6 +34,8 @@ linters-settings: - G112 - G201 - G203 + testifylint: + enable-all: true issues: exclude-rules: diff --git a/binding/binding_msgpack_test.go b/binding/binding_msgpack_test.go index a6cd6aa83d..a8116391fe 100644 --- a/binding/binding_msgpack_test.go +++ b/binding/binding_msgpack_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/ugorji/go/codec" ) @@ -24,7 +25,7 @@ func TestBindingMsgPack(t *testing.T) { buf := bytes.NewBuffer([]byte{}) assert.NotNil(t, buf) err := codec.NewEncoder(buf, h).Encode(test) - assert.NoError(t, err) + require.NoError(t, err) data := buf.Bytes() @@ -41,14 +42,14 @@ func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, req := requestWithBody("POST", path, body) req.Header.Add("Content-Type", MIMEMSGPACK) err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "bar", obj.Foo) obj = FooStruct{} req = requestWithBody("POST", badPath, badBody) req.Header.Add("Content-Type", MIMEMSGPACK) err = MsgPack.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestBindingDefaultMsgPack(t *testing.T) { diff --git a/binding/binding_test.go b/binding/binding_test.go index c59e5e939a..2036b59b91 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -20,6 +20,7 @@ import ( "github.com/gin-gonic/gin/testdata/protoexample" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) @@ -175,7 +176,7 @@ func TestBindingJSONNilBody(t *testing.T) { var obj FooStruct req, _ := http.NewRequest(http.MethodPost, "/", nil) err := JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestBindingJSON(t *testing.T) { @@ -376,7 +377,7 @@ func TestBindingFormStringSliceMap(t *testing.T) { req := requestWithBody("POST", "/", "foo=something&foo=bar&hello=world") req.Header.Add("Content-Type", MIMEPOSTForm) err := Form.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, obj) assert.Len(t, obj, 2) target := map[string][]string{ @@ -389,7 +390,7 @@ func TestBindingFormStringSliceMap(t *testing.T) { req = requestWithBody("POST", "/", "foo=something&foo=bar&hello=world") req.Header.Add("Content-Type", MIMEPOSTForm) err = Form.Bind(req, &objInvalid) - assert.Error(t, err) + require.Error(t, err) } func TestBindingQuery(t *testing.T) { @@ -428,7 +429,7 @@ func TestBindingQueryStringMap(t *testing.T) { obj := make(map[string]string) req := requestWithBody("GET", "/?foo=bar&hello=world", "") err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, obj) assert.Len(t, obj, 2) assert.Equal(t, "bar", obj["foo"]) @@ -437,7 +438,7 @@ func TestBindingQueryStringMap(t *testing.T) { obj = make(map[string]string) req = requestWithBody("GET", "/?foo=bar&foo=2&hello=world", "") // should pick last err = b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, obj) assert.Len(t, obj, 2) assert.Equal(t, "2", obj["foo"]) @@ -495,28 +496,28 @@ func TestBindingYAMLFail(t *testing.T) { func createFormPostRequest(t *testing.T) *http.Request { req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo")) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", MIMEPOSTForm) return req } func createDefaultFormPostRequest(t *testing.T) *http.Request { req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar")) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", MIMEPOSTForm) return req } func createFormPostRequestForMap(t *testing.T) *http.Request { req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}")) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", MIMEPOSTForm) return req } func createFormPostRequestForMapFail(t *testing.T) *http.Request { req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello")) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", MIMEPOSTForm) return req } @@ -527,20 +528,20 @@ func createFormFilesMultipartRequest(t *testing.T) *http.Request { mw := multipart.NewWriter(body) defer mw.Close() - assert.NoError(t, mw.SetBoundary(boundary)) - assert.NoError(t, mw.WriteField("foo", "bar")) - assert.NoError(t, mw.WriteField("bar", "foo")) + require.NoError(t, mw.SetBoundary(boundary)) + require.NoError(t, mw.WriteField("foo", "bar")) + require.NoError(t, mw.WriteField("bar", "foo")) f, err := os.Open("form.go") - assert.NoError(t, err) + require.NoError(t, err) defer f.Close() fw, err1 := mw.CreateFormFile("file", "form.go") - assert.NoError(t, err1) + require.NoError(t, err1) _, err = io.Copy(fw, f) - assert.NoError(t, err) + require.NoError(t, err) req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) - assert.NoError(t, err2) + require.NoError(t, err2) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) return req @@ -552,20 +553,20 @@ func createFormFilesMultipartRequestFail(t *testing.T) *http.Request { mw := multipart.NewWriter(body) defer mw.Close() - assert.NoError(t, mw.SetBoundary(boundary)) - assert.NoError(t, mw.WriteField("foo", "bar")) - assert.NoError(t, mw.WriteField("bar", "foo")) + require.NoError(t, mw.SetBoundary(boundary)) + require.NoError(t, mw.WriteField("foo", "bar")) + require.NoError(t, mw.WriteField("bar", "foo")) f, err := os.Open("form.go") - assert.NoError(t, err) + require.NoError(t, err) defer f.Close() fw, err1 := mw.CreateFormFile("file_foo", "form_foo.go") - assert.NoError(t, err1) + require.NoError(t, err1) _, err = io.Copy(fw, f) - assert.NoError(t, err) + require.NoError(t, err) req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) - assert.NoError(t, err2) + require.NoError(t, err2) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) return req @@ -577,11 +578,11 @@ func createFormMultipartRequest(t *testing.T) *http.Request { mw := multipart.NewWriter(body) defer mw.Close() - assert.NoError(t, mw.SetBoundary(boundary)) - assert.NoError(t, mw.WriteField("foo", "bar")) - assert.NoError(t, mw.WriteField("bar", "foo")) + require.NoError(t, mw.SetBoundary(boundary)) + require.NoError(t, mw.WriteField("foo", "bar")) + require.NoError(t, mw.WriteField("bar", "foo")) req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) return req } @@ -592,10 +593,10 @@ func createFormMultipartRequestForMap(t *testing.T) *http.Request { mw := multipart.NewWriter(body) defer mw.Close() - assert.NoError(t, mw.SetBoundary(boundary)) - assert.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}")) + require.NoError(t, mw.SetBoundary(boundary)) + require.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}")) req, err := http.NewRequest("POST", "/?map_foo=getfoo", body) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) return req } @@ -606,10 +607,10 @@ func createFormMultipartRequestForMapFail(t *testing.T) *http.Request { mw := multipart.NewWriter(body) defer mw.Close() - assert.NoError(t, mw.SetBoundary(boundary)) - assert.NoError(t, mw.WriteField("map_foo", "3.14")) + require.NoError(t, mw.SetBoundary(boundary)) + require.NoError(t, mw.WriteField("map_foo", "3.14")) req, err := http.NewRequest("POST", "/?map_foo=getfoo", body) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) return req } @@ -617,7 +618,7 @@ func createFormMultipartRequestForMapFail(t *testing.T) *http.Request { func TestBindingFormPost(t *testing.T) { req := createFormPostRequest(t) var obj FooBarStruct - assert.NoError(t, FormPost.Bind(req, &obj)) + require.NoError(t, FormPost.Bind(req, &obj)) assert.Equal(t, "form-urlencoded", FormPost.Name()) assert.Equal(t, "bar", obj.Foo) @@ -627,7 +628,7 @@ func TestBindingFormPost(t *testing.T) { func TestBindingDefaultValueFormPost(t *testing.T) { req := createDefaultFormPostRequest(t) var obj FooDefaultBarStruct - assert.NoError(t, FormPost.Bind(req, &obj)) + require.NoError(t, FormPost.Bind(req, &obj)) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, "hello", obj.Bar) @@ -637,22 +638,22 @@ func TestBindingFormPostForMap(t *testing.T) { req := createFormPostRequestForMap(t) var obj FooStructForMapType err := FormPost.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64)) + require.NoError(t, err) + assert.InDelta(t, float64(123), obj.MapFoo["bar"].(float64), 0.01) } func TestBindingFormPostForMapFail(t *testing.T) { req := createFormPostRequestForMapFail(t) var obj FooStructForMapType err := FormPost.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestBindingFormFilesMultipart(t *testing.T) { req := createFormFilesMultipartRequest(t) var obj FooBarFileStruct err := FormMultipart.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) // file from os f, _ := os.Open("form.go") @@ -664,9 +665,9 @@ func TestBindingFormFilesMultipart(t *testing.T) { defer mf.Close() fileExpect, _ := io.ReadAll(mf) - assert.Equal(t, FormMultipart.Name(), "multipart/form-data") - assert.Equal(t, obj.Foo, "bar") - assert.Equal(t, obj.Bar, "foo") + assert.Equal(t, "multipart/form-data", FormMultipart.Name()) + assert.Equal(t, "bar", obj.Foo) + assert.Equal(t, "foo", obj.Bar) assert.Equal(t, fileExpect, fileActual) } @@ -674,13 +675,13 @@ func TestBindingFormFilesMultipartFail(t *testing.T) { req := createFormFilesMultipartRequestFail(t) var obj FooBarFileFailStruct err := FormMultipart.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestBindingFormMultipart(t *testing.T) { req := createFormMultipartRequest(t) var obj FooBarStruct - assert.NoError(t, FormMultipart.Bind(req, &obj)) + require.NoError(t, FormMultipart.Bind(req, &obj)) assert.Equal(t, "multipart/form-data", FormMultipart.Name()) assert.Equal(t, "bar", obj.Foo) @@ -691,17 +692,17 @@ func TestBindingFormMultipartForMap(t *testing.T) { req := createFormMultipartRequestForMap(t) var obj FooStructForMapType err := FormMultipart.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64)) + require.NoError(t, err) + assert.InDelta(t, float64(123), obj.MapFoo["bar"].(float64), 0.01) assert.Equal(t, "thinkerou", obj.MapFoo["name"].(string)) - assert.Equal(t, float64(3.14), obj.MapFoo["pai"].(float64)) + assert.InDelta(t, float64(3.14), obj.MapFoo["pai"].(float64), 0.01) } func TestBindingFormMultipartForMapFail(t *testing.T) { req := createFormMultipartRequestForMapFail(t) var obj FooStructForMapType err := FormMultipart.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestBindingProtoBuf(t *testing.T) { @@ -732,7 +733,7 @@ func TestValidationFails(t *testing.T) { var obj FooStruct req := requestWithBody("POST", "/", `{"bar": "foo"}`) err := JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestValidationDisabled(t *testing.T) { @@ -743,7 +744,7 @@ func TestValidationDisabled(t *testing.T) { var obj FooStruct req := requestWithBody("POST", "/", `{"bar": "foo"}`) err := JSON.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) } func TestRequiredSucceeds(t *testing.T) { @@ -754,7 +755,7 @@ func TestRequiredSucceeds(t *testing.T) { var obj HogeStruct req := requestWithBody("POST", "/", `{"hoge": 0}`) err := JSON.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) } func TestRequiredFails(t *testing.T) { @@ -765,7 +766,7 @@ func TestRequiredFails(t *testing.T) { var obj HogeStruct req := requestWithBody("POST", "/", `{"boen": 0}`) err := JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestHeaderBinding(t *testing.T) { @@ -779,7 +780,7 @@ func TestHeaderBinding(t *testing.T) { var theader tHeader req := requestWithBody("GET", "/", "") req.Header.Add("limit", "1000") - assert.NoError(t, h.Bind(req, &theader)) + require.NoError(t, h.Bind(req, &theader)) assert.Equal(t, 1000, theader.Limit) req = requestWithBody("GET", "/", "") @@ -790,7 +791,7 @@ func TestHeaderBinding(t *testing.T) { } err := h.Bind(req, &failStruct{}) - assert.Error(t, err) + require.Error(t, err) } func TestUriBinding(t *testing.T) { @@ -803,14 +804,14 @@ func TestUriBinding(t *testing.T) { var tag Tag m := make(map[string][]string) m["name"] = []string{"thinkerou"} - assert.NoError(t, b.BindUri(m, &tag)) + require.NoError(t, b.BindUri(m, &tag)) assert.Equal(t, "thinkerou", tag.Name) type NotSupportStruct struct { Name map[string]any `uri:"name"` } var not NotSupportStruct - assert.Error(t, b.BindUri(m, ¬)) + require.Error(t, b.BindUri(m, ¬)) assert.Equal(t, map[string]any(nil), not.Name) } @@ -831,9 +832,9 @@ func TestUriInnerBinding(t *testing.T) { } var tag Tag - assert.NoError(t, Uri.BindUri(m, &tag)) - assert.Equal(t, tag.Name, expectedName) - assert.Equal(t, tag.S.Age, expectedAge) + require.NoError(t, Uri.BindUri(m, &tag)) + assert.Equal(t, expectedName, tag.Name) + assert.Equal(t, expectedAge, tag.S.Age) } func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, badBody string) { @@ -846,7 +847,7 @@ func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, ba req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 1, obj.Page) assert.Equal(t, 2, obj.Size) assert.Equal(t, "test-appkey", obj.Appkey) @@ -862,14 +863,14 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, "foo", obj.Bar) obj = FooBarStruct{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badBody string) { @@ -882,14 +883,14 @@ func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badB req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, "hello", obj.Bar) obj = FooDefaultBarStruct{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestFormBindingFail(t *testing.T) { @@ -899,18 +900,18 @@ func TestFormBindingFail(t *testing.T) { obj := FooBarStruct{} req, _ := http.NewRequest("POST", "/", nil) err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestFormBindingMultipartFail(t *testing.T) { obj := FooBarStruct{} req, err := http.NewRequest("POST", "/", strings.NewReader("foo=bar")) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+";boundary=testboundary") _, err = req.MultipartReader() - assert.NoError(t, err) + require.NoError(t, err) err = Form.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestFormPostBindingFail(t *testing.T) { @@ -920,7 +921,7 @@ func TestFormPostBindingFail(t *testing.T) { obj := FooBarStruct{} req, _ := http.NewRequest("POST", "/", nil) err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func TestFormMultipartBindingFail(t *testing.T) { @@ -930,7 +931,7 @@ func TestFormMultipartBindingFail(t *testing.T) { obj := FooBarStruct{} req, _ := http.NewRequest("POST", "/", nil) err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody string) { @@ -944,7 +945,7 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s } err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, int64(1510675200), obj.TimeFoo.Unix()) assert.Equal(t, "Asia/Chongqing", obj.TimeFoo.Location().String()) assert.Equal(t, int64(-62135596800), obj.TimeBar.Unix()) @@ -955,7 +956,7 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s obj = FooBarStructForTimeType{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testFormBindingForTimeNotUnixFormat(t *testing.T, method, path, badPath, body, badBody string) { @@ -968,12 +969,12 @@ func testFormBindingForTimeNotUnixFormat(t *testing.T, method, path, badPath, bo req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) obj = FooStructForTimeTypeNotUnixFormat{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body, badBody string) { @@ -986,12 +987,12 @@ func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body, req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) obj = FooStructForTimeTypeNotFormat{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body, badBody string) { @@ -1004,12 +1005,12 @@ func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body, req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) obj = FooStructForTimeTypeFailFormat{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, body, badBody string) { @@ -1022,12 +1023,12 @@ func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, bod req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) obj = FooStructForTimeTypeFailLocation{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBody string) { @@ -1040,7 +1041,7 @@ func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBo req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Nil(t, obj.Foo) } @@ -1055,13 +1056,13 @@ func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBo req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "", obj.TestName) obj = InvalidNameType{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badBody string) { @@ -1074,12 +1075,12 @@ func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badB req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) obj = InvalidNameMapType{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody string, typ string) { @@ -1094,17 +1095,17 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s case "Slice": obj := FooStructForSliceType{} err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, []int{1, 2}, obj.SliceFoo) obj = FooStructForSliceType{} req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) case "Struct": obj := FooStructForStructType{} err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, struct { Idx int "form:\"idx\"" @@ -1113,7 +1114,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s case "StructPointer": obj := FooStructForStructPointerType{} err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, struct { Name string "form:\"name\"" @@ -1122,33 +1123,33 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s case "Map": obj := FooStructForMapType{} err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64)) + require.NoError(t, err) + assert.InDelta(t, float64(123), obj.MapFoo["bar"].(float64), 0.01) case "SliceMap": obj := FooStructForSliceMapType{} err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) case "Ptr": obj := FooStructForStringPtrType{} err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Nil(t, obj.PtrFoo) assert.Equal(t, "test", *obj.PtrBar) obj = FooStructForStringPtrType{} obj.PtrBar = new(string) err = b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "test", *obj.PtrBar) objErr := FooStructForMapPtrType{} err = b.Bind(req, &objErr) - assert.Error(t, err) + require.Error(t, err) obj = FooStructForStringPtrType{} req = requestWithBody(method, badPath, badBody) err = b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } } @@ -1162,7 +1163,7 @@ func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string) req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, "foo", obj.Bar) } @@ -1177,7 +1178,7 @@ func testQueryBindingFail(t *testing.T, method, path, badPath, body, badBody str req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testQueryBindingBoolFail(t *testing.T, method, path, badPath, body, badBody string) { @@ -1190,7 +1191,7 @@ func testQueryBindingBoolFail(t *testing.T, method, path, badPath, body, badBody req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { @@ -1199,13 +1200,13 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody obj := FooStruct{} req := requestWithBody("POST", path, body) err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "bar", obj.Foo) obj = FooStruct{} req = requestWithBody("POST", badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testBodyBindingSlice(t *testing.T, b Binding, name, path, badPath, body, badBody string) { @@ -1214,12 +1215,12 @@ func testBodyBindingSlice(t *testing.T, b Binding, name, path, badPath, body, ba var obj1 []FooStruct req := requestWithBody("POST", path, body) err := b.Bind(req, &obj1) - assert.NoError(t, err) + require.NoError(t, err) var obj2 []FooStruct req = requestWithBody("POST", badPath, badBody) err = JSON.Bind(req, &obj2) - assert.Error(t, err) + require.Error(t, err) } func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) { @@ -1229,7 +1230,7 @@ func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badB req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, obj) assert.Len(t, obj, 2) assert.Equal(t, "bar", obj["foo"]) @@ -1239,13 +1240,13 @@ func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badB obj = make(map[string]string) req = requestWithBody("POST", badPath, badBody) err = b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } objInt := make(map[string]int) req = requestWithBody("POST", path, body) err = b.Bind(req, &objInt) - assert.Error(t, err) + require.Error(t, err) } func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body, badBody string) { @@ -1255,16 +1256,16 @@ func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body req := requestWithBody("POST", path, body) EnableDecoderUseNumber = true err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) // we hope it is int64(123) v, e := obj.Foo.(json.Number).Int64() - assert.NoError(t, e) + require.NoError(t, e) assert.Equal(t, int64(123), v) obj = FooStructUseNumber{} req = requestWithBody("POST", badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, body, badBody string) { @@ -1274,15 +1275,15 @@ func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, bod req := requestWithBody("POST", path, body) EnableDecoderUseNumber = false err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) // it will return float64(123) if not use EnableDecoderUseNumber // maybe it is not hoped - assert.Equal(t, float64(123), obj.Foo) + assert.InDelta(t, float64(123), obj.Foo, 0.01) obj = FooStructUseNumber{} req = requestWithBody("POST", badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testBodyBindingDisallowUnknownFields(t *testing.T, b Binding, path, badPath, body, badBody string) { @@ -1294,13 +1295,13 @@ func testBodyBindingDisallowUnknownFields(t *testing.T, b Binding, path, badPath obj := FooStructDisallowUnknownFields{} req := requestWithBody("POST", path, body) err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "bar", obj.Foo) obj = FooStructDisallowUnknownFields{} req = requestWithBody("POST", badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) assert.Contains(t, err.Error(), "what") } @@ -1310,13 +1311,13 @@ func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, bad obj := FooStruct{} req := requestWithBody("POST", path, body) err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) assert.Equal(t, "", obj.Foo) obj = FooStruct{} req = requestWithBody("POST", badPath, badBody) err = JSON.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { @@ -1326,14 +1327,14 @@ func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, ba req := requestWithBody("POST", path, body) req.Header.Add("Content-Type", MIMEPROTOBUF) err := b.Bind(req, &obj) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "yes", *obj.Label) obj = protoexample.Test{} req = requestWithBody("POST", badPath, badBody) req.Header.Add("Content-Type", MIMEPROTOBUF) err = ProtoBuf.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } type hook struct{} @@ -1358,28 +1359,28 @@ func TestPlainBinding(t *testing.T) { var s string req := requestWithBody("POST", "/", "test string") - assert.NoError(t, p.Bind(req, &s)) - assert.Equal(t, s, "test string") + require.NoError(t, p.Bind(req, &s)) + assert.Equal(t, "test string", s) var bs []byte req = requestWithBody("POST", "/", "test []byte") - assert.NoError(t, p.Bind(req, &bs)) + require.NoError(t, p.Bind(req, &bs)) assert.Equal(t, bs, []byte("test []byte")) var i int req = requestWithBody("POST", "/", "test fail") - assert.Error(t, p.Bind(req, &i)) + require.Error(t, p.Bind(req, &i)) req = requestWithBody("POST", "/", "") req.Body = &failRead{} - assert.Error(t, p.Bind(req, &s)) + require.Error(t, p.Bind(req, &s)) req = requestWithBody("POST", "/", "") - assert.Nil(t, p.Bind(req, nil)) + require.NoError(t, p.Bind(req, nil)) var ptr *string req = requestWithBody("POST", "/", "") - assert.Nil(t, p.Bind(req, ptr)) + require.NoError(t, p.Bind(req, ptr)) } func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) { @@ -1391,20 +1392,20 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body req.Body = io.NopCloser(&hook{}) req.Header.Add("Content-Type", MIMEPROTOBUF) err := b.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) invalidobj := FooStruct{} req.Body = io.NopCloser(strings.NewReader(`{"msg":"hello"}`)) req.Header.Add("Content-Type", MIMEPROTOBUF) err = b.Bind(req, &invalidobj) - assert.Error(t, err) - assert.Equal(t, err.Error(), "obj is not ProtoMessage") + require.Error(t, err) + assert.Equal(t, "obj is not ProtoMessage", err.Error()) obj = protoexample.Test{} req = requestWithBody("POST", badPath, badBody) req.Header.Add("Content-Type", MIMEPROTOBUF) err = ProtoBuf.Bind(req, &obj) - assert.Error(t, err) + require.Error(t, err) } func requestWithBody(method, path, body string) (req *http.Request) { diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index afd51f9d88..9ea0895cd8 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestMappingBaseTypes(t *testing.T) { @@ -59,7 +60,7 @@ func TestMappingBaseTypes(t *testing.T) { field := val.Elem().Type().Field(0) _, err := mapping(val, emptyField, formSource{field.Name: {tt.form}}, "form") - assert.NoError(t, err, testName) + require.NoError(t, err, testName) actual := val.Elem().Field(0).Interface() assert.Equal(t, tt.expect, actual, testName) @@ -73,7 +74,7 @@ func TestMappingDefault(t *testing.T) { Array [1]int `form:",default=9"` } err := mappingByPtr(&s, formSource{}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 9, s.Int) assert.Equal(t, []int{9}, s.Slice) @@ -85,7 +86,7 @@ func TestMappingSkipField(t *testing.T) { A int } err := mappingByPtr(&s, formSource{}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 0, s.A) } @@ -96,7 +97,7 @@ func TestMappingIgnoreField(t *testing.T) { B int `form:"-"` } err := mappingByPtr(&s, formSource{"A": {"9"}, "B": {"9"}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 9, s.A) assert.Equal(t, 0, s.B) @@ -108,7 +109,7 @@ func TestMappingUnexportedField(t *testing.T) { b int `form:"b"` } err := mappingByPtr(&s, formSource{"a": {"9"}, "b": {"9"}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 9, s.A) assert.Equal(t, 0, s.b) @@ -119,7 +120,7 @@ func TestMappingPrivateField(t *testing.T) { f int `form:"field"` } err := mappingByPtr(&s, formSource{"field": {"6"}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 0, s.f) } @@ -129,7 +130,7 @@ func TestMappingUnknownFieldType(t *testing.T) { } err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form") - assert.Error(t, err) + require.Error(t, err) assert.Equal(t, errUnknownType, err) } @@ -138,7 +139,7 @@ func TestMappingURI(t *testing.T) { F int `uri:"field"` } err := mapURI(&s, map[string][]string{"field": {"6"}}) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 6, s.F) } @@ -147,7 +148,7 @@ func TestMappingForm(t *testing.T) { F int `form:"field"` } err := mapForm(&s, map[string][]string{"field": {"6"}}) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 6, s.F) } @@ -156,7 +157,7 @@ func TestMapFormWithTag(t *testing.T) { F int `externalTag:"field"` } err := MapFormWithTag(&s, map[string][]string{"field": {"6"}}, "externalTag") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 6, s.F) } @@ -171,7 +172,7 @@ func TestMappingTime(t *testing.T) { var err error time.Local, err = time.LoadLocation("Europe/Berlin") - assert.NoError(t, err) + require.NoError(t, err) err = mapForm(&s, map[string][]string{ "Time": {"2019-01-20T16:02:58Z"}, @@ -180,7 +181,7 @@ func TestMappingTime(t *testing.T) { "CSTTime": {"2019-01-20"}, "UTCTime": {"2019-01-20"}, }) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "2019-01-20 16:02:58 +0000 UTC", s.Time.String()) assert.Equal(t, "2019-01-20 00:00:00 +0100 CET", s.LocalTime.String()) @@ -195,14 +196,14 @@ func TestMappingTime(t *testing.T) { Time time.Time `time_location:"wrong"` } err = mapForm(&wrongLoc, map[string][]string{"Time": {"2019-01-20T16:02:58Z"}}) - assert.Error(t, err) + require.Error(t, err) // wrong time value var wrongTime struct { Time time.Time } err = mapForm(&wrongTime, map[string][]string{"Time": {"wrong"}}) - assert.Error(t, err) + require.Error(t, err) } func TestMappingTimeDuration(t *testing.T) { @@ -212,12 +213,12 @@ func TestMappingTimeDuration(t *testing.T) { // ok err := mappingByPtr(&s, formSource{"D": {"5s"}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 5*time.Second, s.D) // error err = mappingByPtr(&s, formSource{"D": {"wrong"}}, "form") - assert.Error(t, err) + require.Error(t, err) } func TestMappingSlice(t *testing.T) { @@ -227,17 +228,17 @@ func TestMappingSlice(t *testing.T) { // default value err := mappingByPtr(&s, formSource{}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, []int{9}, s.Slice) // ok err = mappingByPtr(&s, formSource{"slice": {"3", "4"}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, []int{3, 4}, s.Slice) // error err = mappingByPtr(&s, formSource{"slice": {"wrong"}}, "form") - assert.Error(t, err) + require.Error(t, err) } func TestMappingArray(t *testing.T) { @@ -247,20 +248,20 @@ func TestMappingArray(t *testing.T) { // wrong default err := mappingByPtr(&s, formSource{}, "form") - assert.Error(t, err) + require.Error(t, err) // ok err = mappingByPtr(&s, formSource{"array": {"3", "4"}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, [2]int{3, 4}, s.Array) // error - not enough vals err = mappingByPtr(&s, formSource{"array": {"3"}}, "form") - assert.Error(t, err) + require.Error(t, err) // error - wrong value err = mappingByPtr(&s, formSource{"array": {"wrong"}}, "form") - assert.Error(t, err) + require.Error(t, err) } func TestMappingStructField(t *testing.T) { @@ -271,7 +272,7 @@ func TestMappingStructField(t *testing.T) { } err := mappingByPtr(&s, formSource{"J": {`{"I": 9}`}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 9, s.J.I) } @@ -289,20 +290,20 @@ func TestMappingPtrField(t *testing.T) { // With 0 items. var req0 ptrRequest err = mappingByPtr(&req0, formSource{}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Empty(t, req0.Items) // With 1 item. var req1 ptrRequest err = mappingByPtr(&req1, formSource{"items": {`{"key": 1}`}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Len(t, req1.Items, 1) assert.EqualValues(t, 1, req1.Items[0].Key) // With 2 items. var req2 ptrRequest err = mappingByPtr(&req2, formSource{"items": {`{"key": 1}`, `{"key": 2}`}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Len(t, req2.Items, 2) assert.EqualValues(t, 1, req2.Items[0].Key) assert.EqualValues(t, 2, req2.Items[1].Key) @@ -314,7 +315,7 @@ func TestMappingMapField(t *testing.T) { } err := mappingByPtr(&s, formSource{"M": {`{"one": 1}`}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, map[string]int{"one": 1}, s.M) } @@ -325,7 +326,7 @@ func TestMappingIgnoredCircularRef(t *testing.T) { var s S err := mappingByPtr(&s, formSource{}, "form") - assert.NoError(t, err) + require.NoError(t, err) } type customUnmarshalParamHex int @@ -344,7 +345,7 @@ func TestMappingCustomUnmarshalParamHexWithFormTag(t *testing.T) { Foo customUnmarshalParamHex `form:"foo"` } err := mappingByPtr(&s, formSource{"foo": {`f5`}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, 245, s.Foo) } @@ -354,7 +355,7 @@ func TestMappingCustomUnmarshalParamHexWithURITag(t *testing.T) { Foo customUnmarshalParamHex `uri:"foo"` } err := mappingByPtr(&s, formSource{"foo": {`f5`}}, "uri") - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, 245, s.Foo) } @@ -381,7 +382,7 @@ func TestMappingCustomStructTypeWithFormTag(t *testing.T) { FileData customUnmarshalParamType `form:"data"` } err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, "file", s.FileData.Protocol) assert.EqualValues(t, "/foo", s.FileData.Path) @@ -393,7 +394,7 @@ func TestMappingCustomStructTypeWithURITag(t *testing.T) { FileData customUnmarshalParamType `uri:"data"` } err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri") - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, "file", s.FileData.Protocol) assert.EqualValues(t, "/foo", s.FileData.Path) @@ -405,7 +406,7 @@ func TestMappingCustomPointerStructTypeWithFormTag(t *testing.T) { FileData *customUnmarshalParamType `form:"data"` } err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, "file", s.FileData.Protocol) assert.EqualValues(t, "/foo", s.FileData.Path) @@ -417,7 +418,7 @@ func TestMappingCustomPointerStructTypeWithURITag(t *testing.T) { FileData *customUnmarshalParamType `uri:"data"` } err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri") - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, "file", s.FileData.Protocol) assert.EqualValues(t, "/foo", s.FileData.Path) @@ -442,7 +443,7 @@ func TestMappingCustomSliceUri(t *testing.T) { FileData customPath `uri:"path"` } err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "uri") - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, "bar", s.FileData[0]) assert.EqualValues(t, "foo", s.FileData[1]) @@ -453,7 +454,7 @@ func TestMappingCustomSliceForm(t *testing.T) { FileData customPath `form:"path"` } err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "form") - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, "bar", s.FileData[0]) assert.EqualValues(t, "foo", s.FileData[1]) @@ -492,7 +493,7 @@ func TestMappingCustomArrayUri(t *testing.T) { } val := `664a062ac74a8ad104e0e80f` err := mappingByPtr(&s, formSource{"id": {val}}, "uri") - assert.NoError(t, err) + require.NoError(t, err) expected, _ := convertTo(val) assert.EqualValues(t, expected, s.FileData) @@ -504,7 +505,7 @@ func TestMappingCustomArrayForm(t *testing.T) { } val := `664a062ac74a8ad104e0e80f` err := mappingByPtr(&s, formSource{"id": {val}}, "form") - assert.NoError(t, err) + require.NoError(t, err) expected, _ := convertTo(val) assert.EqualValues(t, expected, s.FileData) diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go index 4e97c0f0a6..9782b81df8 100644 --- a/binding/multipart_form_mapping_test.go +++ b/binding/multipart_form_mapping_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestFormMultipartBindingBindOneFile(t *testing.T) { @@ -27,7 +28,7 @@ func TestFormMultipartBindingBindOneFile(t *testing.T) { req := createRequestMultipartFiles(t, file) err := FormMultipart.Bind(req, &s) - assert.NoError(t, err) + require.NoError(t, err) assertMultipartFileHeader(t, &s.FileValue, file) assertMultipartFileHeader(t, s.FilePtr, file) @@ -53,7 +54,7 @@ func TestFormMultipartBindingBindTwoFiles(t *testing.T) { req := createRequestMultipartFiles(t, files...) err := FormMultipart.Bind(req, &s) - assert.NoError(t, err) + require.NoError(t, err) assert.Len(t, s.SliceValues, len(files)) assert.Len(t, s.SlicePtrs, len(files)) @@ -90,7 +91,7 @@ func TestFormMultipartBindingBindError(t *testing.T) { } { req := createRequestMultipartFiles(t, files...) err := FormMultipart.Bind(req, tt.s) - assert.Error(t, err) + require.Error(t, err) } } @@ -106,17 +107,17 @@ func createRequestMultipartFiles(t *testing.T, files ...testFile) *http.Request mw := multipart.NewWriter(&body) for _, file := range files { fw, err := mw.CreateFormFile(file.Fieldname, file.Filename) - assert.NoError(t, err) + require.NoError(t, err) n, err := fw.Write(file.Content) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, len(file.Content), n) } err := mw.Close() - assert.NoError(t, err) + require.NoError(t, err) req, err := http.NewRequest("POST", "/", &body) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+mw.Boundary()) return req @@ -127,12 +128,12 @@ func assertMultipartFileHeader(t *testing.T, fh *multipart.FileHeader, file test assert.Equal(t, int64(len(file.Content)), fh.Size) fl, err := fh.Open() - assert.NoError(t, err) + require.NoError(t, err) body, err := io.ReadAll(fl) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, string(file.Content), string(body)) err = fl.Close() - assert.NoError(t, err) + require.NoError(t, err) } diff --git a/binding/validate_test.go b/binding/validate_test.go index 1fc15ff079..c9bbe6011e 100644 --- a/binding/validate_test.go +++ b/binding/validate_test.go @@ -11,6 +11,7 @@ import ( "github.com/go-playground/validator/v10" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type testInterface interface { @@ -113,10 +114,10 @@ func TestValidateNoValidationValues(t *testing.T) { test := createNoValidationValues() empty := structNoValidationValues{} - assert.Nil(t, validate(test)) - assert.Nil(t, validate(&test)) - assert.Nil(t, validate(empty)) - assert.Nil(t, validate(&empty)) + require.NoError(t, validate(test)) + require.NoError(t, validate(&test)) + require.NoError(t, validate(empty)) + require.NoError(t, validate(&empty)) assert.Equal(t, origin, test) } @@ -163,8 +164,8 @@ func TestValidateNoValidationPointers(t *testing.T) { //assert.Nil(t, validate(test)) //assert.Nil(t, validate(&test)) - assert.Nil(t, validate(empty)) - assert.Nil(t, validate(&empty)) + require.NoError(t, validate(empty)) + require.NoError(t, validate(&empty)) //assert.Equal(t, origin, test) } @@ -173,22 +174,22 @@ type Object map[string]any func TestValidatePrimitives(t *testing.T) { obj := Object{"foo": "bar", "bar": 1} - assert.NoError(t, validate(obj)) - assert.NoError(t, validate(&obj)) + require.NoError(t, validate(obj)) + require.NoError(t, validate(&obj)) assert.Equal(t, Object{"foo": "bar", "bar": 1}, obj) obj2 := []Object{{"foo": "bar", "bar": 1}, {"foo": "bar", "bar": 1}} - assert.NoError(t, validate(obj2)) - assert.NoError(t, validate(&obj2)) + require.NoError(t, validate(obj2)) + require.NoError(t, validate(&obj2)) nu := 10 - assert.NoError(t, validate(nu)) - assert.NoError(t, validate(&nu)) + require.NoError(t, validate(nu)) + require.NoError(t, validate(&nu)) assert.Equal(t, 10, nu) str := "value" - assert.NoError(t, validate(str)) - assert.NoError(t, validate(&str)) + require.NoError(t, validate(str)) + require.NoError(t, validate(&str)) assert.Equal(t, "value", str) } @@ -212,8 +213,8 @@ func TestValidateAndModifyStruct(t *testing.T) { s := structModifyValidation{Integer: 1} errs := validate(&s) - assert.Nil(t, errs) - assert.Equal(t, s, structModifyValidation{Integer: 0}) + require.NoError(t, errs) + assert.Equal(t, structModifyValidation{Integer: 0}, s) } // structCustomValidation is a helper struct we use to check that @@ -239,14 +240,14 @@ func TestValidatorEngine(t *testing.T) { err := engine.RegisterValidation("notone", notOne) // Check that we can register custom validation without error - assert.Nil(t, err) + require.NoError(t, err) // Create an instance which will fail validation withOne := structCustomValidation{Integer: 1} errs := validate(withOne) // Check that we got back non-nil errs - assert.NotNil(t, errs) + require.Error(t, errs) // Check that the error matches expectation - assert.Error(t, errs, "", "", "notone") + require.Error(t, errs, "", "", "notone") } diff --git a/context_test.go b/context_test.go index 8bbf270086..66190b302e 100644 --- a/context_test.go +++ b/context_test.go @@ -27,6 +27,7 @@ import ( "github.com/gin-gonic/gin/binding" testdata "github.com/gin-gonic/gin/testdata/protoexample" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) @@ -74,20 +75,18 @@ func TestContextFormFile(t *testing.T) { buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) w, err := mw.CreateFormFile("file", "test") - if assert.NoError(t, err) { - _, err = w.Write([]byte("test")) - assert.NoError(t, err) - } + require.NoError(t, err) + _, err = w.Write([]byte("test")) + require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") - if assert.NoError(t, err) { - assert.Equal(t, "test", f.Filename) - } + require.NoError(t, err) + assert.Equal(t, "test", f.Filename) - assert.NoError(t, c.SaveUploadedFile(f, "test")) + require.NoError(t, c.SaveUploadedFile(f, "test")) } func TestContextFormFileFailed(t *testing.T) { @@ -99,29 +98,27 @@ func TestContextFormFileFailed(t *testing.T) { c.Request.Header.Set("Content-Type", mw.FormDataContentType()) c.engine.MaxMultipartMemory = 8 << 20 f, err := c.FormFile("file") - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, f) } func TestContextMultipartForm(t *testing.T) { buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) - assert.NoError(t, mw.WriteField("foo", "bar")) + require.NoError(t, mw.WriteField("foo", "bar")) w, err := mw.CreateFormFile("file", "test") - if assert.NoError(t, err) { - _, err = w.Write([]byte("test")) - assert.NoError(t, err) - } + require.NoError(t, err) + _, err = w.Write([]byte("test")) + require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.MultipartForm() - if assert.NoError(t, err) { - assert.NotNil(t, f) - } + require.NoError(t, err) + assert.NotNil(t, f) - assert.NoError(t, c.SaveUploadedFile(f.File["file"][0], "test")) + require.NoError(t, c.SaveUploadedFile(f.File["file"][0], "test")) } func TestSaveUploadedOpenFailed(t *testing.T) { @@ -136,27 +133,25 @@ func TestSaveUploadedOpenFailed(t *testing.T) { f := &multipart.FileHeader{ Filename: "file", } - assert.Error(t, c.SaveUploadedFile(f, "test")) + require.Error(t, c.SaveUploadedFile(f, "test")) } func TestSaveUploadedCreateFailed(t *testing.T) { buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) w, err := mw.CreateFormFile("file", "test") - if assert.NoError(t, err) { - _, err = w.Write([]byte("test")) - assert.NoError(t, err) - } + require.NoError(t, err) + _, err = w.Write([]byte("test")) + require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") - if assert.NoError(t, err) { - assert.Equal(t, "test", f.Filename) - } + require.NoError(t, err) + assert.Equal(t, "test", f.Filename) - assert.Error(t, c.SaveUploadedFile(f, "/")) + require.Error(t, c.SaveUploadedFile(f, "/")) } func TestContextReset(t *testing.T) { @@ -174,10 +169,10 @@ func TestContextReset(t *testing.T) { assert.False(t, c.IsAborted()) assert.Nil(t, c.Keys) assert.Nil(t, c.Accepted) - assert.Len(t, c.Errors, 0) + assert.Empty(t, c.Errors) assert.Empty(t, c.Errors.Errors()) assert.Empty(t, c.Errors.ByType(ErrorTypeAny)) - assert.Len(t, c.Params, 0) + assert.Empty(t, c.Params) assert.EqualValues(t, c.index, -1) assert.Equal(t, c.Writer.(*responseWriter), &c.writermem) } @@ -230,13 +225,13 @@ func TestContextSetGetValues(t *testing.T) { var a any = 1 c.Set("intInterface", a) - assert.Exactly(t, c.MustGet("string").(string), "this is a string") + assert.Exactly(t, "this is a string", c.MustGet("string").(string)) assert.Exactly(t, c.MustGet("int32").(int32), int32(-42)) - assert.Exactly(t, c.MustGet("int64").(int64), int64(42424242424242)) - assert.Exactly(t, c.MustGet("uint64").(uint64), uint64(42)) - assert.Exactly(t, c.MustGet("float32").(float32), float32(4.2)) - assert.Exactly(t, c.MustGet("float64").(float64), 4.2) - assert.Exactly(t, c.MustGet("intInterface").(int), 1) + assert.Exactly(t, int64(42424242424242), c.MustGet("int64").(int64)) + assert.Exactly(t, uint64(42), c.MustGet("uint64").(uint64)) + assert.InDelta(t, float32(4.2), c.MustGet("float32").(float32), 0.01) + assert.InDelta(t, 4.2, c.MustGet("float64").(float64), 0.01) + assert.Exactly(t, 1, c.MustGet("intInterface").(int)) } func TestContextGetString(t *testing.T) { @@ -278,7 +273,7 @@ func TestContextGetUint64(t *testing.T) { func TestContextGetFloat64(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Set("float64", 4.2) - assert.Equal(t, 4.2, c.GetFloat64("float64")) + assert.InDelta(t, 4.2, c.GetFloat64("float64"), 0.01) } func TestContextGetTime(t *testing.T) { @@ -344,12 +339,12 @@ func TestContextCopy(t *testing.T) { assert.Nil(t, cp.writermem.ResponseWriter) assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter)) assert.Equal(t, cp.Request, c.Request) - assert.Equal(t, cp.index, abortIndex) + assert.Equal(t, abortIndex, cp.index) assert.Equal(t, cp.Keys, c.Keys) assert.Equal(t, cp.engine, c.engine) assert.Equal(t, cp.Params, c.Params) cp.Set("foo", "notBar") - assert.False(t, cp.Keys["foo"] == c.Keys["foo"]) + assert.NotEqual(t, cp.Keys["foo"], c.Keys["foo"]) assert.Equal(t, cp.fullPath, c.fullPath) } @@ -366,7 +361,7 @@ func TestContextHandlerNames(t *testing.T) { names := c.HandlerNames() - assert.True(t, len(names) == 4) + assert.Len(t, names, 4) for _, name := range names { assert.Regexp(t, `^(.*/vendor/)?(github\.com/gin-gonic/gin\.){1}(TestContextHandlerNames\.func.*){0,1}(handlerNameTest.*){0,1}`, name) } @@ -425,7 +420,7 @@ func TestContextQuery(t *testing.T) { func TestContextInitQueryCache(t *testing.T) { validURL, err := url.Parse("https://github.com/gin-gonic/gin/pull/3969?key=value&otherkey=othervalue") - assert.Nil(t, err) + require.NoError(t, err) tests := []struct { testName string @@ -531,7 +526,7 @@ func TestContextQueryAndPostForm(t *testing.T) { Both string `form:"both"` Array []string `form:"array[]"` } - assert.NoError(t, c.Bind(&obj)) + require.NoError(t, c.Bind(&obj)) assert.Equal(t, "bar", obj.Foo, "bar") assert.Equal(t, "main", obj.ID, "main") assert.Equal(t, 11, obj.Page, 11) @@ -548,10 +543,10 @@ func TestContextQueryAndPostForm(t *testing.T) { assert.Equal(t, "second", values[1]) values = c.QueryArray("nokey") - assert.Equal(t, 0, len(values)) + assert.Empty(t, values) values = c.QueryArray("both") - assert.Equal(t, 1, len(values)) + assert.Len(t, values, 1) assert.Equal(t, "GET", values[0]) dicts, ok := c.GetQueryMap("ids") @@ -561,22 +556,22 @@ func TestContextQueryAndPostForm(t *testing.T) { dicts, ok = c.GetQueryMap("nokey") assert.False(t, ok) - assert.Equal(t, 0, len(dicts)) + assert.Empty(t, dicts) dicts, ok = c.GetQueryMap("both") assert.False(t, ok) - assert.Equal(t, 0, len(dicts)) + assert.Empty(t, dicts) dicts, ok = c.GetQueryMap("array") assert.False(t, ok) - assert.Equal(t, 0, len(dicts)) + assert.Empty(t, dicts) dicts = c.QueryMap("ids") assert.Equal(t, "hi", dicts["a"]) assert.Equal(t, "3.14", dicts["b"]) dicts = c.QueryMap("nokey") - assert.Equal(t, 0, len(dicts)) + assert.Empty(t, dicts) } func TestContextPostFormMultipart(t *testing.T) { @@ -594,7 +589,7 @@ func TestContextPostFormMultipart(t *testing.T) { TimeLocation time.Time `form:"time_location" time_format:"02/01/2006 15:04" time_location:"Asia/Tokyo"` BlankTime time.Time `form:"blank_time" time_format:"02/01/2006 15:04"` } - assert.NoError(t, c.Bind(&obj)) + require.NoError(t, c.Bind(&obj)) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, "10", obj.Bar) assert.Equal(t, 10, obj.BarAsInt) @@ -648,10 +643,10 @@ func TestContextPostFormMultipart(t *testing.T) { assert.Equal(t, "second", values[1]) values = c.PostFormArray("nokey") - assert.Equal(t, 0, len(values)) + assert.Empty(t, values) values = c.PostFormArray("foo") - assert.Equal(t, 1, len(values)) + assert.Len(t, values, 1) assert.Equal(t, "bar", values[0]) dicts, ok := c.GetPostFormMap("names") @@ -661,14 +656,14 @@ func TestContextPostFormMultipart(t *testing.T) { dicts, ok = c.GetPostFormMap("nokey") assert.False(t, ok) - assert.Equal(t, 0, len(dicts)) + assert.Empty(t, dicts) dicts = c.PostFormMap("names") assert.Equal(t, "thinkerou", dicts["a"]) assert.Equal(t, "tianou", dicts["b"]) dicts = c.PostFormMap("nokey") - assert.Equal(t, 0, len(dicts)) + assert.Empty(t, dicts) } func TestContextSetCookie(t *testing.T) { @@ -693,7 +688,7 @@ func TestContextGetCookie(t *testing.T) { assert.Equal(t, "gin", cookie) _, err := c.Cookie("nokey") - assert.Error(t, err) + require.Error(t, err) } func TestContextBodyAllowedForStatus(t *testing.T) { @@ -798,7 +793,7 @@ func TestContextRenderNoContentAPIJSON(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, w.Header().Get("Content-Type"), "application/vnd.api+json") + assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type")) } // Tests that the response is serialized as JSON @@ -1160,7 +1155,7 @@ func TestContextRenderProtoBuf(t *testing.T) { c.ProtoBuf(http.StatusCreated, data) protoData, err := proto.Marshal(data) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, string(protoData), w.Body.String()) @@ -1321,7 +1316,7 @@ func TestContextNegotiationNotSupport(t *testing.T) { }) assert.Equal(t, http.StatusNotAcceptable, w.Code) - assert.Equal(t, c.index, abortIndex) + assert.Equal(t, abortIndex, c.index) assert.True(t, c.IsAborted()) } @@ -1349,23 +1344,23 @@ func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) { c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.Header.Add("Accept", "*/*") - assert.Equal(t, c.NegotiateFormat("*/*"), "*/*") - assert.Equal(t, c.NegotiateFormat("text/*"), "text/*") - assert.Equal(t, c.NegotiateFormat("application/*"), "application/*") - assert.Equal(t, c.NegotiateFormat(MIMEJSON), MIMEJSON) - assert.Equal(t, c.NegotiateFormat(MIMEXML), MIMEXML) - assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML) + assert.Equal(t, "*/*", c.NegotiateFormat("*/*")) + assert.Equal(t, "text/*", c.NegotiateFormat("text/*")) + assert.Equal(t, "application/*", c.NegotiateFormat("application/*")) + assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON)) + assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML)) + assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML)) c, _ = CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.Header.Add("Accept", "text/*") - assert.Equal(t, c.NegotiateFormat("*/*"), "*/*") - assert.Equal(t, c.NegotiateFormat("text/*"), "text/*") - assert.Equal(t, c.NegotiateFormat("application/*"), "") - assert.Equal(t, c.NegotiateFormat(MIMEJSON), "") - assert.Equal(t, c.NegotiateFormat(MIMEXML), "") - assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML) + assert.Equal(t, "*/*", c.NegotiateFormat("*/*")) + assert.Equal(t, "text/*", c.NegotiateFormat("text/*")) + assert.Equal(t, "", c.NegotiateFormat("application/*")) + assert.Equal(t, "", c.NegotiateFormat(MIMEJSON)) + assert.Equal(t, "", c.NegotiateFormat(MIMEXML)) + assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML)) } func TestContextNegotiationFormatCustom(t *testing.T) { @@ -1444,7 +1439,7 @@ func TestContextAbortWithStatusJSON(t *testing.T) { buf := new(bytes.Buffer) _, err := buf.ReadFrom(w.Body) - assert.NoError(t, err) + require.NoError(t, err) jsonStringBody := buf.String() assert.Equal(t, "{\"foo\":\"fooValue\",\"bar\":\"barValue\"}", jsonStringBody) } @@ -1669,7 +1664,7 @@ func TestContextAutoBindJSON(t *testing.T) { Foo string `json:"foo"` Bar string `json:"bar"` } - assert.NoError(t, c.Bind(&obj)) + require.NoError(t, c.Bind(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Empty(t, c.Errors) @@ -1686,7 +1681,7 @@ func TestContextBindWithJSON(t *testing.T) { Foo string `json:"foo"` Bar string `json:"bar"` } - assert.NoError(t, c.BindJSON(&obj)) + require.NoError(t, c.BindJSON(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, 0, w.Body.Len()) @@ -1707,7 +1702,7 @@ func TestContextBindWithXML(t *testing.T) { Foo string `xml:"foo"` Bar string `xml:"bar"` } - assert.NoError(t, c.BindXML(&obj)) + require.NoError(t, c.BindXML(&obj)) assert.Equal(t, "FOO", obj.Foo) assert.Equal(t, "BAR", obj.Bar) assert.Equal(t, 0, w.Body.Len()) @@ -1722,7 +1717,7 @@ func TestContextBindPlain(t *testing.T) { var s string - assert.NoError(t, c.BindPlain(&s)) + require.NoError(t, c.BindPlain(&s)) assert.Equal(t, "test string", s) assert.Equal(t, 0, w.Body.Len()) @@ -1732,7 +1727,7 @@ func TestContextBindPlain(t *testing.T) { var bs []byte - assert.NoError(t, c.BindPlain(&bs)) + require.NoError(t, c.BindPlain(&bs)) assert.Equal(t, []byte("test []byte"), bs) assert.Equal(t, 0, w.Body.Len()) } @@ -1752,7 +1747,7 @@ func TestContextBindHeader(t *testing.T) { Limit int `header:"limit"` } - assert.NoError(t, c.BindHeader(&testHeader)) + require.NoError(t, c.BindHeader(&testHeader)) assert.Equal(t, 8000, testHeader.Rate) assert.Equal(t, "music", testHeader.Domain) assert.Equal(t, 1000, testHeader.Limit) @@ -1769,7 +1764,7 @@ func TestContextBindWithQuery(t *testing.T) { Foo string `form:"foo"` Bar string `form:"bar"` } - assert.NoError(t, c.BindQuery(&obj)) + require.NoError(t, c.BindQuery(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, 0, w.Body.Len()) @@ -1786,7 +1781,7 @@ func TestContextBindWithYAML(t *testing.T) { Foo string `yaml:"foo"` Bar string `yaml:"bar"` } - assert.NoError(t, c.BindYAML(&obj)) + require.NoError(t, c.BindYAML(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, 0, w.Body.Len()) @@ -1803,7 +1798,7 @@ func TestContextBindWithTOML(t *testing.T) { Foo string `toml:"foo"` Bar string `toml:"bar"` } - assert.NoError(t, c.BindTOML(&obj)) + require.NoError(t, c.BindTOML(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, 0, w.Body.Len()) @@ -1821,7 +1816,7 @@ func TestContextBadAutoBind(t *testing.T) { } assert.False(t, c.IsAborted()) - assert.Error(t, c.Bind(&obj)) + require.Error(t, c.Bind(&obj)) c.Writer.WriteHeaderNow() assert.Empty(t, obj.Bar) @@ -1839,7 +1834,7 @@ func TestContextAutoShouldBindJSON(t *testing.T) { Foo string `json:"foo"` Bar string `json:"bar"` } - assert.NoError(t, c.ShouldBind(&obj)) + require.NoError(t, c.ShouldBind(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Empty(t, c.Errors) @@ -1856,7 +1851,7 @@ func TestContextShouldBindWithJSON(t *testing.T) { Foo string `json:"foo"` Bar string `json:"bar"` } - assert.NoError(t, c.ShouldBindJSON(&obj)) + require.NoError(t, c.ShouldBindJSON(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, 0, w.Body.Len()) @@ -1877,7 +1872,7 @@ func TestContextShouldBindWithXML(t *testing.T) { Foo string `xml:"foo"` Bar string `xml:"bar"` } - assert.NoError(t, c.ShouldBindXML(&obj)) + require.NoError(t, c.ShouldBindXML(&obj)) assert.Equal(t, "FOO", obj.Foo) assert.Equal(t, "BAR", obj.Bar) assert.Equal(t, 0, w.Body.Len()) @@ -1892,7 +1887,7 @@ func TestContextShouldBindPlain(t *testing.T) { var s string - assert.NoError(t, c.ShouldBindPlain(&s)) + require.NoError(t, c.ShouldBindPlain(&s)) assert.Equal(t, "test string", s) assert.Equal(t, 0, w.Body.Len()) // []byte @@ -1902,7 +1897,7 @@ func TestContextShouldBindPlain(t *testing.T) { var bs []byte - assert.NoError(t, c.ShouldBindPlain(&bs)) + require.NoError(t, c.ShouldBindPlain(&bs)) assert.Equal(t, []byte("test []byte"), bs) assert.Equal(t, 0, w.Body.Len()) } @@ -1922,7 +1917,7 @@ func TestContextShouldBindHeader(t *testing.T) { Limit int `header:"limit"` } - assert.NoError(t, c.ShouldBindHeader(&testHeader)) + require.NoError(t, c.ShouldBindHeader(&testHeader)) assert.Equal(t, 8000, testHeader.Rate) assert.Equal(t, "music", testHeader.Domain) assert.Equal(t, 1000, testHeader.Limit) @@ -1941,7 +1936,7 @@ func TestContextShouldBindWithQuery(t *testing.T) { Foo1 string `form:"Foo"` Bar1 string `form:"Bar"` } - assert.NoError(t, c.ShouldBindQuery(&obj)) + require.NoError(t, c.ShouldBindQuery(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, "foo1", obj.Bar1) @@ -1960,7 +1955,7 @@ func TestContextShouldBindWithYAML(t *testing.T) { Foo string `yaml:"foo"` Bar string `yaml:"bar"` } - assert.NoError(t, c.ShouldBindYAML(&obj)) + require.NoError(t, c.ShouldBindYAML(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, 0, w.Body.Len()) @@ -1977,7 +1972,7 @@ func TestContextShouldBindWithTOML(t *testing.T) { Foo string `toml:"foo"` Bar string `toml:"bar"` } - assert.NoError(t, c.ShouldBindTOML(&obj)) + require.NoError(t, c.ShouldBindTOML(&obj)) assert.Equal(t, "foo", obj.Bar) assert.Equal(t, "bar", obj.Foo) assert.Equal(t, 0, w.Body.Len()) @@ -1995,7 +1990,7 @@ func TestContextBadAutoShouldBind(t *testing.T) { } assert.False(t, c.IsAborted()) - assert.Error(t, c.ShouldBind(&obj)) + require.Error(t, c.ShouldBind(&obj)) assert.Empty(t, obj.Bar) assert.Empty(t, obj.Foo) @@ -2056,10 +2051,10 @@ func TestContextShouldBindBodyWith(t *testing.T) { // When it binds to typeA and typeB, it finds the body is // not typeB but typeA. objA := typeA{} - assert.NoError(t, c.ShouldBindBodyWith(&objA, tt.bindingA)) + require.NoError(t, c.ShouldBindBodyWith(&objA, tt.bindingA)) assert.Equal(t, typeA{"FOO"}, objA) objB := typeB{} - assert.Error(t, c.ShouldBindBodyWith(&objB, tt.bindingB)) + require.Error(t, c.ShouldBindBodyWith(&objB, tt.bindingB)) assert.NotEqual(t, typeB{"BAR"}, objB) } // bodyB to typeA and typeB @@ -2072,10 +2067,10 @@ func TestContextShouldBindBodyWith(t *testing.T) { "POST", "http://example.com", bytes.NewBufferString(tt.bodyB), ) objA := typeA{} - assert.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA)) + require.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA)) assert.NotEqual(t, typeA{"FOO"}, objA) objB := typeB{} - assert.NoError(t, c.ShouldBindBodyWith(&objB, tt.bindingB)) + require.NoError(t, c.ShouldBindBodyWith(&objB, tt.bindingB)) assert.Equal(t, typeB{"BAR"}, objB) } } @@ -2124,22 +2119,22 @@ func TestContextShouldBindBodyWithJSON(t *testing.T) { objJSON := typeJSON{} if tt.bindingBody == binding.JSON { - assert.NoError(t, c.ShouldBindBodyWithJSON(&objJSON)) + require.NoError(t, c.ShouldBindBodyWithJSON(&objJSON)) assert.Equal(t, typeJSON{"FOO"}, objJSON) } if tt.bindingBody == binding.XML { - assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + require.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) assert.Equal(t, typeJSON{}, objJSON) } if tt.bindingBody == binding.YAML { - assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + require.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) assert.Equal(t, typeJSON{}, objJSON) } if tt.bindingBody == binding.TOML { - assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + require.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) assert.Equal(t, typeJSON{}, objJSON) } } @@ -2188,22 +2183,22 @@ func TestContextShouldBindBodyWithXML(t *testing.T) { objXML := typeXML{} if tt.bindingBody == binding.JSON { - assert.Error(t, c.ShouldBindBodyWithXML(&objXML)) + require.Error(t, c.ShouldBindBodyWithXML(&objXML)) assert.Equal(t, typeXML{}, objXML) } if tt.bindingBody == binding.XML { - assert.NoError(t, c.ShouldBindBodyWithXML(&objXML)) + require.NoError(t, c.ShouldBindBodyWithXML(&objXML)) assert.Equal(t, typeXML{"FOO"}, objXML) } if tt.bindingBody == binding.YAML { - assert.Error(t, c.ShouldBindBodyWithXML(&objXML)) + require.Error(t, c.ShouldBindBodyWithXML(&objXML)) assert.Equal(t, typeXML{}, objXML) } if tt.bindingBody == binding.TOML { - assert.Error(t, c.ShouldBindBodyWithXML(&objXML)) + require.Error(t, c.ShouldBindBodyWithXML(&objXML)) assert.Equal(t, typeXML{}, objXML) } } @@ -2253,22 +2248,22 @@ func TestContextShouldBindBodyWithYAML(t *testing.T) { // YAML belongs to a super collection of JSON, so JSON can be parsed by YAML if tt.bindingBody == binding.JSON { - assert.NoError(t, c.ShouldBindBodyWithYAML(&objYAML)) + require.NoError(t, c.ShouldBindBodyWithYAML(&objYAML)) assert.Equal(t, typeYAML{"FOO"}, objYAML) } if tt.bindingBody == binding.XML { - assert.Error(t, c.ShouldBindBodyWithYAML(&objYAML)) + require.Error(t, c.ShouldBindBodyWithYAML(&objYAML)) assert.Equal(t, typeYAML{}, objYAML) } if tt.bindingBody == binding.YAML { - assert.NoError(t, c.ShouldBindBodyWithYAML(&objYAML)) + require.NoError(t, c.ShouldBindBodyWithYAML(&objYAML)) assert.Equal(t, typeYAML{"FOO"}, objYAML) } if tt.bindingBody == binding.TOML { - assert.Error(t, c.ShouldBindBodyWithYAML(&objYAML)) + require.Error(t, c.ShouldBindBodyWithYAML(&objYAML)) assert.Equal(t, typeYAML{}, objYAML) } } @@ -2317,22 +2312,22 @@ func TestContextShouldBindBodyWithTOML(t *testing.T) { objTOML := typeTOML{} if tt.bindingBody == binding.JSON { - assert.Error(t, c.ShouldBindBodyWithTOML(&objTOML)) + require.Error(t, c.ShouldBindBodyWithTOML(&objTOML)) assert.Equal(t, typeTOML{}, objTOML) } if tt.bindingBody == binding.XML { - assert.Error(t, c.ShouldBindBodyWithTOML(&objTOML)) + require.Error(t, c.ShouldBindBodyWithTOML(&objTOML)) assert.Equal(t, typeTOML{}, objTOML) } if tt.bindingBody == binding.YAML { - assert.Error(t, c.ShouldBindBodyWithTOML(&objTOML)) + require.Error(t, c.ShouldBindBodyWithTOML(&objTOML)) assert.Equal(t, typeTOML{}, objTOML) } if tt.bindingBody == binding.TOML { - assert.NoError(t, c.ShouldBindBodyWithTOML(&objTOML)) + require.NoError(t, c.ShouldBindBodyWithTOML(&objTOML)) assert.Equal(t, typeTOML{"FOO"}, objTOML) } } @@ -2387,27 +2382,27 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) { if tt.bindingBody == binding.Plain { body := "" - assert.NoError(t, c.ShouldBindBodyWithPlain(&body)) - assert.Equal(t, body, "foo=FOO") + require.NoError(t, c.ShouldBindBodyWithPlain(&body)) + assert.Equal(t, "foo=FOO", body) } if tt.bindingBody == binding.JSON { - assert.NoError(t, c.ShouldBindBodyWithJSON(&objJSON)) + require.NoError(t, c.ShouldBindBodyWithJSON(&objJSON)) assert.Equal(t, typeJSON{"FOO"}, objJSON) } if tt.bindingBody == binding.XML { - assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + require.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) assert.Equal(t, typeJSON{}, objJSON) } if tt.bindingBody == binding.YAML { - assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + require.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) assert.Equal(t, typeJSON{}, objJSON) } if tt.bindingBody == binding.TOML { - assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) + require.Error(t, c.ShouldBindBodyWithJSON(&objJSON)) assert.Equal(t, typeJSON{}, objJSON) } } @@ -2416,10 +2411,10 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) { func TestContextGolangContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) - assert.NoError(t, c.Err()) + require.NoError(t, c.Err()) assert.Nil(t, c.Done()) ti, ok := c.Deadline() - assert.Equal(t, ti, time.Time{}) + assert.Equal(t, time.Time{}, ti) assert.False(t, ok) assert.Equal(t, c.Value(ContextRequestKey), c.Request) assert.Equal(t, c.Value(ContextKey), c) @@ -2468,7 +2463,7 @@ func TestContextGetRawData(t *testing.T) { c.Request.Header.Add("Content-Type", MIMEPOSTForm) data, err := c.GetRawData() - assert.Nil(t, err) + require.NoError(t, err) assert.Equal(t, "Fetch binary post data", string(data)) } @@ -2539,7 +2534,7 @@ func TestContextStream(t *testing.T) { }() _, err := w.Write([]byte("test")) - assert.NoError(t, err) + require.NoError(t, err) return stopStream }) @@ -2557,7 +2552,7 @@ func TestContextStreamWithClientGone(t *testing.T) { }() _, err := writer.Write([]byte("test")) - assert.NoError(t, err) + require.NoError(t, err) return true }) @@ -2685,7 +2680,7 @@ func TestContextWithFallbackErrFromRequestContext(t *testing.T) { // enable ContextWithFallback feature flag c.engine.ContextWithFallback = true - assert.Nil(t, c.Err()) + require.NoError(t, c.Err()) c2, _ := CreateTestContext(httptest.NewRecorder()) // enable ContextWithFallback feature flag @@ -2834,7 +2829,7 @@ func TestContextAddParam(t *testing.T) { c.AddParam(id, value) v, ok := c.Params.Get(id) - assert.Equal(t, ok, true) + assert.True(t, ok) assert.Equal(t, value, v) } diff --git a/debug_test.go b/debug_test.go index 1e576681ec..edf4bb12ec 100644 --- a/debug_test.go +++ b/debug_test.go @@ -17,6 +17,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // TODO @@ -154,13 +155,13 @@ func TestGetMinVer(t *testing.T) { var m uint64 var e error _, e = getMinVer("go1") - assert.NotNil(t, e) + require.Error(t, e) m, e = getMinVer("go1.1") assert.Equal(t, uint64(1), m) - assert.Nil(t, e) + require.NoError(t, e) m, e = getMinVer("go1.1.1") - assert.Nil(t, e) + require.NoError(t, e) assert.Equal(t, uint64(1), m) _, e = getMinVer("go1.1.1.1") - assert.NotNil(t, e) + require.Error(t, e) } diff --git a/errors_test.go b/errors_test.go index f77a634296..72a3699289 100644 --- a/errors_test.go +++ b/errors_test.go @@ -11,6 +11,7 @@ import ( "github.com/gin-gonic/gin/internal/json" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestError(t *testing.T) { @@ -122,7 +123,7 @@ func TestErrorUnwrap(t *testing.T) { }) // check that 'errors.Is()' and 'errors.As()' behave as expected : - assert.True(t, errors.Is(err, innerErr)) + require.ErrorIs(t, err, innerErr) var testErr TestErr - assert.True(t, errors.As(err, &testErr)) + require.ErrorAs(t, err, &testErr) } diff --git a/fs_test.go b/fs_test.go index a1690cd928..167ac1afa1 100644 --- a/fs_test.go +++ b/fs_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type mockFileSystem struct { @@ -28,7 +29,7 @@ func TestOnlyFilesFS_Open(t *testing.T) { file, err := fs.Open("foo") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, testFile, file.(neutralizedReaddirFile).File) } @@ -43,7 +44,7 @@ func TestOnlyFilesFS_Open_err(t *testing.T) { file, err := fs.Open("foo") - assert.ErrorIs(t, err, testError) + require.ErrorIs(t, err, testError) assert.Nil(t, file) } @@ -52,7 +53,7 @@ func Test_neuteredReaddirFile_Readdir(t *testing.T) { res, err := n.Readdir(0) - assert.NoError(t, err) + require.NoError(t, err) assert.Nil(t, res) } diff --git a/gin_integration_test.go b/gin_integration_test.go index 539827120e..3082bc2caa 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -21,6 +21,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // params[0]=url example:http://127.0.0.1:8080/index (cannot be empty) @@ -40,11 +41,11 @@ func testRequest(t *testing.T, params ...string) { client := &http.Client{Transport: tr} resp, err := client.Get(params[0]) - assert.NoError(t, err) + require.NoError(t, err) defer resp.Body.Close() body, ioerr := io.ReadAll(resp.Body) - assert.NoError(t, ioerr) + require.NoError(t, ioerr) var responseStatus = "200 OK" if len(params) > 1 && params[1] != "" { @@ -73,13 +74,13 @@ func TestRunEmpty(t *testing.T) { // otherwise the main thread will complete time.Sleep(5 * time.Millisecond) - assert.Error(t, router.Run(":8080")) + require.Error(t, router.Run(":8080")) testRequest(t, "http://localhost:8080/example") } func TestBadTrustedCIDRs(t *testing.T) { router := New() - assert.Error(t, router.SetTrustedProxies([]string{"hello/world"})) + require.Error(t, router.SetTrustedProxies([]string{"hello/world"})) } /* legacy tests @@ -87,7 +88,7 @@ func TestBadTrustedCIDRsForRun(t *testing.T) { os.Setenv("PORT", "") router := New() router.TrustedProxies = []string{"hello/world"} - assert.Error(t, router.Run(":8080")) + require.Error(t, router.Run(":8080")) } func TestBadTrustedCIDRsForRunUnix(t *testing.T) { @@ -100,7 +101,7 @@ func TestBadTrustedCIDRsForRunUnix(t *testing.T) { go func() { router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) - assert.Error(t, router.RunUnix(unixTestSocket)) + require.Error(t, router.RunUnix(unixTestSocket)) }() // have to wait for the goroutine to start and run the server // otherwise the main thread will complete @@ -112,15 +113,15 @@ func TestBadTrustedCIDRsForRunFd(t *testing.T) { router.TrustedProxies = []string{"hello/world"} addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - assert.NoError(t, err) + require.NoError(t, err) listener, err := net.ListenTCP("tcp", addr) - assert.NoError(t, err) + require.NoError(t, err) socketFile, err := listener.File() - assert.NoError(t, err) + require.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()))) + require.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 @@ -132,12 +133,12 @@ func TestBadTrustedCIDRsForRunListener(t *testing.T) { router.TrustedProxies = []string{"hello/world"} addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - assert.NoError(t, err) + require.NoError(t, err) listener, err := net.ListenTCP("tcp", addr) - assert.NoError(t, err) + require.NoError(t, err) go func() { router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) - assert.Error(t, router.RunListener(listener)) + require.Error(t, router.RunListener(listener)) }() // have to wait for the goroutine to start and run the server // otherwise the main thread will complete @@ -148,7 +149,7 @@ 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")) + require.Error(t, router.RunTLS(":8080", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) } */ @@ -164,7 +165,7 @@ func TestRunTLS(t *testing.T) { // otherwise the main thread will complete time.Sleep(5 * time.Millisecond) - assert.Error(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) + require.Error(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) testRequest(t, "https://localhost:8443/example") } @@ -201,7 +202,7 @@ func TestPusher(t *testing.T) { // otherwise the main thread will complete time.Sleep(5 * time.Millisecond) - assert.Error(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) + require.Error(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) testRequest(t, "https://localhost:8449/pusher") } @@ -216,14 +217,14 @@ func TestRunEmptyWithEnv(t *testing.T) { // otherwise the main thread will complete time.Sleep(5 * time.Millisecond) - assert.Error(t, router.Run(":3123")) + require.Error(t, router.Run(":3123")) testRequest(t, "http://localhost:3123/example") } func TestRunTooMuchParams(t *testing.T) { router := New() assert.Panics(t, func() { - assert.NoError(t, router.Run("2", "2")) + require.NoError(t, router.Run("2", "2")) }) } @@ -237,7 +238,7 @@ func TestRunWithPort(t *testing.T) { // otherwise the main thread will complete time.Sleep(5 * time.Millisecond) - assert.Error(t, router.Run(":5150")) + require.Error(t, router.Run(":5150")) testRequest(t, "http://localhost:5150/example") } @@ -257,7 +258,7 @@ func TestUnixSocket(t *testing.T) { time.Sleep(5 * time.Millisecond) c, err := net.Dial("unix", unixTestSocket) - assert.NoError(t, err) + require.NoError(t, err) fmt.Fprint(c, "GET /example HTTP/1.0\r\n\r\n") scanner := bufio.NewScanner(c) @@ -271,7 +272,7 @@ func TestUnixSocket(t *testing.T) { func TestBadUnixSocket(t *testing.T) { router := New() - assert.Error(t, router.RunUnix("#/tmp/unix_unit_test")) + require.Error(t, router.RunUnix("#/tmp/unix_unit_test")) } func TestRunQUIC(t *testing.T) { @@ -286,7 +287,7 @@ func TestRunQUIC(t *testing.T) { // otherwise the main thread will complete time.Sleep(5 * time.Millisecond) - assert.Error(t, router.RunQUIC(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) + require.Error(t, router.RunQUIC(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) testRequest(t, "https://localhost:8443/example") } @@ -294,15 +295,15 @@ func TestFileDescriptor(t *testing.T) { router := New() addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - assert.NoError(t, err) + require.NoError(t, err) listener, err := net.ListenTCP("tcp", addr) - assert.NoError(t, err) + require.NoError(t, err) socketFile, err := listener.File() if isWindows() { // not supported by windows, it is unimplemented now - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } if socketFile == nil { @@ -318,7 +319,7 @@ func TestFileDescriptor(t *testing.T) { time.Sleep(5 * time.Millisecond) c, err := net.Dial("tcp", listener.Addr().String()) - assert.NoError(t, err) + require.NoError(t, err) fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n") scanner := bufio.NewScanner(c) @@ -332,15 +333,15 @@ func TestFileDescriptor(t *testing.T) { func TestBadFileDescriptor(t *testing.T) { router := New() - assert.Error(t, router.RunFd(0)) + require.Error(t, router.RunFd(0)) } func TestListener(t *testing.T) { router := New() addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - assert.NoError(t, err) + require.NoError(t, err) listener, err := net.ListenTCP("tcp", addr) - assert.NoError(t, err) + require.NoError(t, err) go func() { router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) assert.NoError(t, router.RunListener(listener)) @@ -350,7 +351,7 @@ func TestListener(t *testing.T) { time.Sleep(5 * time.Millisecond) c, err := net.Dial("tcp", listener.Addr().String()) - assert.NoError(t, err) + require.NoError(t, err) fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n") scanner := bufio.NewScanner(c) @@ -365,11 +366,11 @@ func TestListener(t *testing.T) { func TestBadListener(t *testing.T) { router := New() addr, err := net.ResolveTCPAddr("tcp", "localhost:10086") - assert.NoError(t, err) + require.NoError(t, err) listener, err := net.ListenTCP("tcp", addr) - assert.NoError(t, err) + require.NoError(t, err) listener.Close() - assert.Error(t, router.RunListener(listener)) + require.Error(t, router.RunListener(listener)) } func TestWithHttptestWithAutoSelectedPort(t *testing.T) { @@ -395,7 +396,14 @@ func TestConcurrentHandleContext(t *testing.T) { wg.Add(iterations) for i := 0; i < iterations; i++ { go func() { - testGetRequestHandler(t, router, "/") + req, err := http.NewRequest(http.MethodGet, "/", nil) + assert.NoError(t, err) + + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + assert.Equal(t, "it worked", w.Body.String(), "resp body should match") + assert.Equal(t, 200, w.Code, "should get a 200") wg.Done() }() } @@ -417,17 +425,6 @@ func TestConcurrentHandleContext(t *testing.T) { // testRequest(t, "http://localhost:8033/example") // } -func testGetRequestHandler(t *testing.T, h http.Handler, url string) { - req, err := http.NewRequest(http.MethodGet, url, nil) - assert.NoError(t, err) - - w := httptest.NewRecorder() - h.ServeHTTP(w, req) - - assert.Equal(t, "it worked", w.Body.String(), "resp body should match") - assert.Equal(t, 200, w.Code, "should get a 200") -} - func TestTreeRunDynamicRouting(t *testing.T) { router := New() router.GET("/aa/*xx", func(c *Context) { c.String(http.StatusOK, "/aa/*xx") }) diff --git a/gin_test.go b/gin_test.go index db70a8c5c7..719f63e454 100644 --- a/gin_test.go +++ b/gin_test.go @@ -20,6 +20,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "golang.org/x/net/http2" ) @@ -547,10 +548,10 @@ func TestEngineHandleContextManyReEntries(t *testing.T) { r.GET("/:count", func(c *Context) { countStr := c.Param("count") count, err := strconv.Atoi(countStr) - assert.NoError(t, err) + require.NoError(t, err) n, err := c.Writer.Write([]byte(".")) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, 1, n) switch { @@ -580,7 +581,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { expectedTrustedCIDRs := []*net.IPNet{parseCIDR("0.0.0.0/0")} err := r.SetTrustedProxies([]string{"0.0.0.0/0"}) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } @@ -588,7 +589,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { { err := r.SetTrustedProxies([]string{"192.168.1.33/33"}) - assert.Error(t, err) + require.Error(t, err) } // valid ipv4 address @@ -597,7 +598,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { err := r.SetTrustedProxies([]string{"192.168.1.33"}) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } @@ -605,7 +606,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { { err := r.SetTrustedProxies([]string{"192.168.1.256"}) - assert.Error(t, err) + require.Error(t, err) } // valid ipv6 address @@ -613,7 +614,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { expectedTrustedCIDRs := []*net.IPNet{parseCIDR("2002:0000:0000:1234:abcd:ffff:c0a8:0101/128")} err := r.SetTrustedProxies([]string{"2002:0000:0000:1234:abcd:ffff:c0a8:0101"}) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } @@ -621,7 +622,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { { err := r.SetTrustedProxies([]string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101"}) - assert.Error(t, err) + require.Error(t, err) } // valid ipv6 cidr @@ -629,7 +630,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { expectedTrustedCIDRs := []*net.IPNet{parseCIDR("::/0")} err := r.SetTrustedProxies([]string{"::/0"}) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } @@ -637,7 +638,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { { err := r.SetTrustedProxies([]string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101/129"}) - assert.Error(t, err) + require.Error(t, err) } // valid combination @@ -653,7 +654,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { "172.16.0.1", }) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs) } @@ -665,7 +666,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { "172.16.0.256", }) - assert.Error(t, err) + require.Error(t, err) } // nil value @@ -673,7 +674,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { err := r.SetTrustedProxies(nil) assert.Nil(t, r.trustedCIDRs) - assert.Nil(t, err) + require.NoError(t, err) } } diff --git a/githubapi_test.go b/githubapi_test.go index 9276bed508..6d34878720 100644 --- a/githubapi_test.go +++ b/githubapi_test.go @@ -14,6 +14,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type route struct { @@ -295,9 +296,9 @@ func TestShouldBindUri(t *testing.T) { } router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { var person Person - assert.NoError(t, c.ShouldBindUri(&person)) - assert.True(t, person.Name != "") - assert.True(t, person.ID != "") + require.NoError(t, c.ShouldBindUri(&person)) + assert.NotEqual(t, "", person.Name) + assert.NotEqual(t, "", person.ID) c.String(http.StatusOK, "ShouldBindUri test OK") }) @@ -317,9 +318,9 @@ func TestBindUri(t *testing.T) { } router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { var person Person - assert.NoError(t, c.BindUri(&person)) - assert.True(t, person.Name != "") - assert.True(t, person.ID != "") + require.NoError(t, c.BindUri(&person)) + assert.NotEqual(t, "", person.Name) + assert.NotEqual(t, "", person.ID) c.String(http.StatusOK, "BindUri test OK") }) @@ -338,7 +339,7 @@ func TestBindUriError(t *testing.T) { } router.Handle(http.MethodGet, "/new/rest/:num", func(c *Context) { var m Member - assert.Error(t, c.BindUri(&m)) + require.Error(t, c.BindUri(&m)) }) path1, _ := exampleFromPath("/new/rest/:num") diff --git a/logger_test.go b/logger_test.go index 6c1814dc11..b05df740a3 100644 --- a/logger_test.go +++ b/logger_test.go @@ -329,13 +329,13 @@ func TestIsOutputColor(t *testing.T) { } consoleColorMode = autoColor - assert.Equal(t, true, p.IsOutputColor()) + assert.True(t, p.IsOutputColor()) ForceConsoleColor() - assert.Equal(t, true, p.IsOutputColor()) + assert.True(t, p.IsOutputColor()) DisableConsoleColor() - assert.Equal(t, false, p.IsOutputColor()) + assert.False(t, p.IsOutputColor()) // test with isTerm flag false. p = LogFormatterParams{ @@ -343,13 +343,13 @@ func TestIsOutputColor(t *testing.T) { } consoleColorMode = autoColor - assert.Equal(t, false, p.IsOutputColor()) + assert.False(t, p.IsOutputColor()) ForceConsoleColor() - assert.Equal(t, true, p.IsOutputColor()) + assert.True(t, p.IsOutputColor()) DisableConsoleColor() - assert.Equal(t, false, p.IsOutputColor()) + assert.False(t, p.IsOutputColor()) // reset console color mode. consoleColorMode = autoColor diff --git a/path_test.go b/path_test.go index 864302f4e1..2269b78ee1 100644 --- a/path_test.go +++ b/path_test.go @@ -87,7 +87,7 @@ func TestPathCleanMallocs(t *testing.T) { for _, test := range cleanTests { allocs := testing.AllocsPerRun(100, func() { cleanPath(test.result) }) - assert.EqualValues(t, allocs, 0) + assert.InDelta(t, 0, allocs, 0.01) } } diff --git a/render/render_msgpack_test.go b/render/render_msgpack_test.go index db4b71e5ee..579897ccc6 100644 --- a/render/render_msgpack_test.go +++ b/render/render_msgpack_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/ugorji/go/codec" ) @@ -29,7 +30,7 @@ func TestRenderMsgPack(t *testing.T) { err := (MsgPack{data}).Render(w) - assert.NoError(t, err) + require.NoError(t, err) h := new(codec.MsgpackHandle) assert.NotNil(t, h) @@ -37,7 +38,7 @@ func TestRenderMsgPack(t *testing.T) { assert.NotNil(t, buf) err = codec.NewEncoder(buf, h).Encode(data) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, w.Body.String(), buf.String()) assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type")) } diff --git a/render/render_test.go b/render/render_test.go index 145f131641..27a5065be7 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -18,6 +18,7 @@ import ( "github.com/gin-gonic/gin/internal/json" testdata "github.com/gin-gonic/gin/testdata/protoexample" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) @@ -36,7 +37,7 @@ func TestRenderJSON(t *testing.T) { err := (JSON{data}).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } @@ -46,7 +47,7 @@ func TestRenderJSONError(t *testing.T) { data := make(chan int) // json: unsupported type: chan int - assert.Error(t, (JSON{data}).Render(w)) + require.Error(t, (JSON{data}).Render(w)) } func TestRenderIndentedJSON(t *testing.T) { @@ -58,7 +59,7 @@ func TestRenderIndentedJSON(t *testing.T) { err := (IndentedJSON{data}).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } @@ -69,7 +70,7 @@ func TestRenderIndentedJSONPanics(t *testing.T) { // json: unsupported type: chan int err := (IndentedJSON{data}).Render(w) - assert.Error(t, err) + require.Error(t, err) } func TestRenderSecureJSON(t *testing.T) { @@ -83,7 +84,7 @@ func TestRenderSecureJSON(t *testing.T) { err1 := (SecureJSON{"while(1);", data}).Render(w1) - assert.NoError(t, err1) + require.NoError(t, err1) assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type")) @@ -95,7 +96,7 @@ func TestRenderSecureJSON(t *testing.T) { }} err2 := (SecureJSON{"while(1);", datas}).Render(w2) - assert.NoError(t, err2) + require.NoError(t, err2) assert.Equal(t, "while(1);[{\"foo\":\"bar\"},{\"bar\":\"foo\"}]", w2.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type")) } @@ -106,7 +107,7 @@ func TestRenderSecureJSONFail(t *testing.T) { // json: unsupported type: chan int err := (SecureJSON{"while(1);", data}).Render(w) - assert.Error(t, err) + require.Error(t, err) } func TestRenderJsonpJSON(t *testing.T) { @@ -120,7 +121,7 @@ func TestRenderJsonpJSON(t *testing.T) { err1 := (JsonpJSON{"x", data}).Render(w1) - assert.NoError(t, err1) + require.NoError(t, err1) assert.Equal(t, "x({\"foo\":\"bar\"});", w1.Body.String()) assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type")) @@ -132,7 +133,7 @@ func TestRenderJsonpJSON(t *testing.T) { }} err2 := (JsonpJSON{"x", datas}).Render(w2) - assert.NoError(t, err2) + require.NoError(t, err2) assert.Equal(t, "x([{\"foo\":\"bar\"},{\"bar\":\"foo\"}]);", w2.Body.String()) assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type")) } @@ -191,7 +192,7 @@ func TestRenderJsonpJSONError2(t *testing.T) { assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type")) e := (JsonpJSON{"", data}).Render(w) - assert.NoError(t, e) + require.NoError(t, e) assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type")) @@ -203,7 +204,7 @@ func TestRenderJsonpJSONFail(t *testing.T) { // json: unsupported type: chan int err := (JsonpJSON{"x", data}).Render(w) - assert.Error(t, err) + require.Error(t, err) } func TestRenderAsciiJSON(t *testing.T) { @@ -215,7 +216,7 @@ func TestRenderAsciiJSON(t *testing.T) { err := (AsciiJSON{data1}).Render(w1) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String()) assert.Equal(t, "application/json", w1.Header().Get("Content-Type")) @@ -223,7 +224,7 @@ func TestRenderAsciiJSON(t *testing.T) { data2 := 3.1415926 err = (AsciiJSON{data2}).Render(w2) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "3.1415926", w2.Body.String()) } @@ -232,7 +233,7 @@ func TestRenderAsciiJSONFail(t *testing.T) { data := make(chan int) // json: unsupported type: chan int - assert.Error(t, (AsciiJSON{data}).Render(w)) + require.Error(t, (AsciiJSON{data}).Render(w)) } func TestRenderPureJSON(t *testing.T) { @@ -242,7 +243,7 @@ func TestRenderPureJSON(t *testing.T) { "html": "", } err := (PureJSON{data}).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\"}\n", w.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } @@ -283,7 +284,7 @@ b: assert.Equal(t, "application/yaml; charset=utf-8", w.Header().Get("Content-Type")) err := (YAML{data}).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "|4-\n a : Easy!\n b:\n \tc: 2\n \td: [3, 4]\n \t\n", w.Body.String()) assert.Equal(t, "application/yaml; charset=utf-8", w.Header().Get("Content-Type")) } @@ -298,7 +299,7 @@ func (ft *fail) MarshalYAML() (any, error) { func TestRenderYAMLFail(t *testing.T) { w := httptest.NewRecorder() err := (YAML{&fail{}}).Render(w) - assert.Error(t, err) + require.Error(t, err) } func TestRenderTOML(t *testing.T) { @@ -311,7 +312,7 @@ func TestRenderTOML(t *testing.T) { assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type")) err := (TOML{data}).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "foo = 'bar'\nhtml = ''\n", w.Body.String()) assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type")) } @@ -319,7 +320,7 @@ func TestRenderTOML(t *testing.T) { func TestRenderTOMLFail(t *testing.T) { w := httptest.NewRecorder() err := (TOML{net.IPv4bcast}).Render(w) - assert.Error(t, err) + require.Error(t, err) } // test Protobuf rendering @@ -334,12 +335,12 @@ func TestRenderProtoBuf(t *testing.T) { (ProtoBuf{data}).WriteContentType(w) protoData, err := proto.Marshal(data) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type")) err = (ProtoBuf{data}).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, string(protoData), w.Body.String()) assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type")) } @@ -348,7 +349,7 @@ func TestRenderProtoBufFail(t *testing.T) { w := httptest.NewRecorder() data := &testdata.Test{} err := (ProtoBuf{data}).Render(w) - assert.Error(t, err) + require.Error(t, err) } func TestRenderXML(t *testing.T) { @@ -362,14 +363,14 @@ func TestRenderXML(t *testing.T) { err := (XML{data}).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "bar", w.Body.String()) assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type")) } func TestRenderRedirect(t *testing.T) { req, err := http.NewRequest("GET", "/test-redirect", nil) - assert.NoError(t, err) + require.NoError(t, err) data1 := Redirect{ Code: http.StatusMovedPermanently, @@ -379,7 +380,7 @@ func TestRenderRedirect(t *testing.T) { w := httptest.NewRecorder() err = data1.Render(w) - assert.NoError(t, err) + require.NoError(t, err) data2 := Redirect{ Code: http.StatusOK, @@ -390,7 +391,7 @@ func TestRenderRedirect(t *testing.T) { w = httptest.NewRecorder() assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() { err := data2.Render(w) - assert.NoError(t, err) + require.NoError(t, err) }) data3 := Redirect{ @@ -401,7 +402,7 @@ func TestRenderRedirect(t *testing.T) { w = httptest.NewRecorder() err = data3.Render(w) - assert.NoError(t, err) + require.NoError(t, err) // only improve coverage data2.WriteContentType(w) @@ -416,7 +417,7 @@ func TestRenderData(t *testing.T) { Data: data, }).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "#!PNG some raw data", w.Body.String()) assert.Equal(t, "image/png", w.Header().Get("Content-Type")) } @@ -435,7 +436,7 @@ func TestRenderString(t *testing.T) { Data: []any{"manu", 2}, }).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "hola manu 2", w.Body.String()) assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) } @@ -448,7 +449,7 @@ func TestRenderStringLenZero(t *testing.T) { Data: []any{}, }).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "hola %s %d", w.Body.String()) assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) } @@ -464,7 +465,7 @@ func TestRenderHTMLTemplate(t *testing.T) { err := instance.Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "Hello alexandernyquist", w.Body.String()) assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } @@ -480,7 +481,7 @@ func TestRenderHTMLTemplateEmptyName(t *testing.T) { err := instance.Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "Hello alexandernyquist", w.Body.String()) assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } @@ -499,7 +500,7 @@ func TestRenderHTMLDebugFiles(t *testing.T) { err := instance.Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "

Hello thinkerou

", w.Body.String()) assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } @@ -518,7 +519,7 @@ func TestRenderHTMLDebugGlob(t *testing.T) { err := instance.Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "

Hello thinkerou

", w.Body.String()) assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } @@ -548,7 +549,7 @@ func TestRenderReader(t *testing.T) { Headers: headers, }).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, body, w.Body.String()) assert.Equal(t, "image/png", w.Header().Get("Content-Type")) assert.Equal(t, strconv.Itoa(len(body)), w.Header().Get("Content-Length")) @@ -571,7 +572,7 @@ func TestRenderReaderNoContentLength(t *testing.T) { Headers: headers, }).Render(w) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, body, w.Body.String()) assert.Equal(t, "image/png", w.Header().Get("Content-Type")) assert.NotContains(t, "Content-Length", w.Header()) @@ -588,6 +589,6 @@ func TestRenderWriteError(t *testing.T) { ResponseRecorder: httptest.NewRecorder(), } err := r.Render(ew) - assert.NotNil(t, err) + require.Error(t, err) assert.Equal(t, `write "my-prefix:" error`, err.Error()) } diff --git a/response_writer_test.go b/response_writer_test.go index 964aa3071e..259b8fa820 100644 --- a/response_writer_test.go +++ b/response_writer_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // TODO @@ -95,13 +96,13 @@ func TestResponseWriterWrite(t *testing.T) { assert.Equal(t, http.StatusOK, w.Status()) assert.Equal(t, http.StatusOK, testWriter.Code) assert.Equal(t, "hola", testWriter.Body.String()) - assert.NoError(t, err) + require.NoError(t, err) n, err = w.Write([]byte(" adios")) assert.Equal(t, 6, n) assert.Equal(t, 10, w.Size()) assert.Equal(t, "hola adios", testWriter.Body.String()) - assert.NoError(t, err) + require.NoError(t, err) } func TestResponseWriterHijack(t *testing.T) { @@ -112,7 +113,7 @@ func TestResponseWriterHijack(t *testing.T) { assert.Panics(t, func() { _, _, err := w.Hijack() - assert.NoError(t, err) + require.NoError(t, err) }) assert.True(t, w.Written()) @@ -135,7 +136,7 @@ func TestResponseWriterFlush(t *testing.T) { // should return 500 resp, err := http.Get(testServer.URL) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) } diff --git a/routes_test.go b/routes_test.go index 73f393e71c..d6233b0904 100644 --- a/routes_test.go +++ b/routes_test.go @@ -13,6 +13,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type header struct { @@ -386,7 +387,7 @@ func TestRouteStaticFile(t *testing.T) { } defer os.Remove(f.Name()) _, err = f.WriteString("Gin Web Framework") - assert.NoError(t, err) + require.NoError(t, err) f.Close() dir, filename := filepath.Split(f.Name()) @@ -421,7 +422,7 @@ func TestRouteStaticFileFS(t *testing.T) { } defer os.Remove(f.Name()) _, err = f.WriteString("Gin Web Framework") - assert.NoError(t, err) + require.NoError(t, err) f.Close() dir, filename := filepath.Split(f.Name()) @@ -484,7 +485,7 @@ func TestRouterMiddlewareAndStatic(t *testing.T) { // Content-Type='text/plain; charset=utf-8' when go version <= 1.16, // else, Content-Type='text/x-go; charset=utf-8' assert.NotEqual(t, "", w.Header().Get("Content-Type")) - assert.NotEqual(t, w.Header().Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST") + assert.NotEqual(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Last-Modified")) assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires")) assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN")) } diff --git a/utils_test.go b/utils_test.go index 058ddb9d70..af0899636d 100644 --- a/utils_test.go +++ b/utils_test.go @@ -145,6 +145,6 @@ func TestMarshalXMLforH(t *testing.T) { } func TestIsASCII(t *testing.T) { - assert.Equal(t, isASCII("test"), true) - assert.Equal(t, isASCII("🧡💛💚💙💜"), false) + assert.True(t, isASCII("test")) + assert.False(t, isASCII("🧡💛💚💙💜")) } From cc4e11438cd6c0bcc632fe3492d3817dfa21c337 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 20:34:34 +0800 Subject: [PATCH 327/346] chore(deps): bump golang.org/x/net from 0.25.0 to 0.27.0 (#4013) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.25.0 to 0.27.0. - [Commits](https://github.com/golang/net/compare/v0.25.0...v0.27.0) --- updated-dependencies: - dependency-name: golang.org/x/net 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 | 12 ++++++------ go.sum | 30 ++++++++++++++++-------------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 4937d2b7cf..035c2dea30 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/quic-go/quic-go v0.43.1 github.com/stretchr/testify v1.9.0 github.com/ugorji/go/codec v1.2.12 - golang.org/x/net v0.25.0 + golang.org/x/net v0.27.0 google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -39,10 +39,10 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect - golang.org/x/mod v0.11.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.9.1 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect ) diff --git a/go.sum b/go.sum index 44af4cc163..55a21627f5 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,8 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -92,24 +92,26 @@ go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 3cb30679b5e3021db16c776ed7e70d380586e9ce Mon Sep 17 00:00:00 2001 From: Jo YoHan <37216082+slowhigh@users.noreply.github.com> Date: Sat, 24 Aug 2024 15:16:30 +0900 Subject: [PATCH 328/346] feat(form): add array collection format in form binding (#3986) * feat(form): add array collection format in form binding * feat(form): add array collection format in form binding * test(form): fix test code for array collection format in form binding --- binding/form_mapping.go | 40 ++++++++++++++++++++++++++ binding/form_mapping_test.go | 39 ++++++++++++++++++++++++++ docs/doc.md | 54 ++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 33389b2889..4a35866da2 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -182,6 +182,38 @@ func trySetCustom(val string, value reflect.Value) (isSet bool, err error) { return false, nil } +func trySplit(vs []string, field reflect.StructField) (newVs []string, err error) { + cfTag := field.Tag.Get("collection_format") + if cfTag == "" || cfTag == "multi" { + return vs, nil + } + + var sep string + switch cfTag { + case "csv": + sep = "," + case "ssv": + sep = " " + case "tsv": + sep = "\t" + case "pipes": + sep = "|" + default: + return vs, fmt.Errorf("%s is not supported in the collection_format. (csv, ssv, pipes)", cfTag) + } + + totalLength := 0 + for _, v := range vs { + totalLength += strings.Count(v, sep) + 1 + } + newVs = make([]string, 0, totalLength) + for _, v := range vs { + newVs = append(newVs, strings.Split(v, sep)...) + } + + return newVs, nil +} + func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSet bool, err error) { vs, ok := form[tagValue] if !ok && !opt.isDefaultExists { @@ -198,6 +230,10 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][ return ok, err } + if vs, err = trySplit(vs, field); err != nil { + return false, err + } + return true, setSlice(vs, value, field) case reflect.Array: if !ok { @@ -208,6 +244,10 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][ return ok, err } + if vs, err = trySplit(vs, field); err != nil { + return false, err + } + if len(vs) != value.Len() { return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String()) } diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 9ea0895cd8..c6db033eee 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -264,6 +264,45 @@ func TestMappingArray(t *testing.T) { require.Error(t, err) } +func TestMappingCollectionFormat(t *testing.T) { + var s struct { + SliceMulti []int `form:"slice_multi" collection_format:"multi"` + SliceCsv []int `form:"slice_csv" collection_format:"csv"` + SliceSsv []int `form:"slice_ssv" collection_format:"ssv"` + SliceTsv []int `form:"slice_tsv" collection_format:"tsv"` + SlicePipes []int `form:"slice_pipes" collection_format:"pipes"` + ArrayMulti [2]int `form:"array_multi" collection_format:"multi"` + ArrayCsv [2]int `form:"array_csv" collection_format:"csv"` + ArraySsv [2]int `form:"array_ssv" collection_format:"ssv"` + ArrayTsv [2]int `form:"array_tsv" collection_format:"tsv"` + ArrayPipes [2]int `form:"array_pipes" collection_format:"pipes"` + } + err := mappingByPtr(&s, formSource{ + "slice_multi": {"1", "2"}, + "slice_csv": {"1,2"}, + "slice_ssv": {"1 2"}, + "slice_tsv": {"1 2"}, + "slice_pipes": {"1|2"}, + "array_multi": {"1", "2"}, + "array_csv": {"1,2"}, + "array_ssv": {"1 2"}, + "array_tsv": {"1 2"}, + "array_pipes": {"1|2"}, + }, "form") + require.NoError(t, err) + + assert.Equal(t, []int{1, 2}, s.SliceMulti) + assert.Equal(t, []int{1, 2}, s.SliceCsv) + assert.Equal(t, []int{1, 2}, s.SliceSsv) + assert.Equal(t, []int{1, 2}, s.SliceTsv) + assert.Equal(t, []int{1, 2}, s.SlicePipes) + assert.Equal(t, [2]int{1, 2}, s.ArrayMulti) + assert.Equal(t, [2]int{1, 2}, s.ArrayCsv) + assert.Equal(t, [2]int{1, 2}, s.ArraySsv) + assert.Equal(t, [2]int{1, 2}, s.ArrayTsv) + assert.Equal(t, [2]int{1, 2}, s.ArrayPipes) +} + func TestMappingStructField(t *testing.T) { var s struct { J struct { diff --git a/docs/doc.md b/docs/doc.md index 5136640929..b76011f2ab 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -26,6 +26,7 @@ - [Custom Validators](#custom-validators) - [Only Bind Query String](#only-bind-query-string) - [Bind Query String or Post Data](#bind-query-string-or-post-data) + - [Collection format for arrays](#collection-format-for-arrays) - [Bind Uri](#bind-uri) - [Bind custom unmarshaler](#bind-custom-unmarshaler) - [Bind Header](#bind-header) @@ -861,6 +862,59 @@ Test it with: curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033" ``` +#### Collection format for arrays + +| Format | Description | Example | +| --------------- | --------------------------------------------------------- | ----------------------- | +| multi (default) | Multiple parameter instances rather than multiple values. | key=foo&key=bar&key=baz | +| csv | Comma-separated values. | foo,bar,baz | +| ssv | Space-separated values. | foo bar baz | +| tsv | Tab-separated values. | "foo\tbar\tbaz" | +| pipes | Pipe-separated values. | foo\|bar\|baz | + +```go +package main + +import ( + "log" + "time" + "github.com/gin-gonic/gin" +) + +type Person struct { + Name string `form:"name"` + Addresses []string `form:"addresses" collection_format:"csv"` + Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` + CreateTime time.Time `form:"createTime" time_format:"unixNano"` + UnixTime time.Time `form:"unixTime" time_format:"unix"` +} + +func main() { + route := gin.Default() + route.GET("/testing", startPage) + route.Run(":8085") +} +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 + if c.ShouldBind(&person) == nil { + log.Println(person.Name) + log.Println(person.Addresses) + log.Println(person.Birthday) + log.Println(person.CreateTime) + log.Println(person.UnixTime) + } + c.String(200, "Success") +} +``` + +Test it with: +```sh +$ curl -X GET "localhost:8085/testing?name=appleboy&addresses=foo,bar&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033" +``` + ### Bind Uri See the [detail information](https://github.com/gin-gonic/gin/issues/846). From 28e57f58b184b2305ace192e02496bb89f6fd8cb Mon Sep 17 00:00:00 2001 From: Ahmad Saeed Goda Date: Fri, 6 Sep 2024 08:21:19 +0300 Subject: [PATCH 329/346] fix(form): Set default value for form fields (#4047) - Use specified default value in struct tags when binding a request input to struct for validation, even if sent empty, not only when not sent at all. - Add string field to `TestMappingDefault` test case. - Add test case for not sent form field to default to the value specified via code. - Add test case for form field sent empty to default to the value specified via code. Fixes: How to apply default value if empty value provided by client during model binding? #4042, #13042df, #a41721a --- binding/form_mapping.go | 3 +++ binding/form_mapping_test.go | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 4a35866da2..a84536f722 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -261,6 +261,9 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][ if len(vs) > 0 { val = vs[0] + if val == "" { + val = opt.defaultValue + } } if ok, err := trySetCustom(val, value); ok { return ok, err diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index c6db033eee..8cf7465173 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -69,6 +69,7 @@ func TestMappingBaseTypes(t *testing.T) { func TestMappingDefault(t *testing.T) { var s struct { + Str string `form:",default=defaultVal"` Int int `form:",default=9"` Slice []int `form:",default=9"` Array [1]int `form:",default=9"` @@ -76,6 +77,7 @@ func TestMappingDefault(t *testing.T) { err := mappingByPtr(&s, formSource{}, "form") require.NoError(t, err) + assert.Equal(t, "defaultVal", s.Str) assert.Equal(t, 9, s.Int) assert.Equal(t, []int{9}, s.Slice) assert.Equal(t, [1]int{9}, s.Array) @@ -152,6 +154,24 @@ func TestMappingForm(t *testing.T) { assert.Equal(t, 6, s.F) } +func TestMappingFormFieldNotSent(t *testing.T) { + var s struct { + F string `form:"field,default=defVal"` + } + err := mapForm(&s, map[string][]string{}) + require.NoError(t, err) + assert.Equal(t, "defVal", s.F) +} + +func TestMappingFormWithEmptyToDefault(t *testing.T) { + var s struct { + F string `form:"field,default=DefVal"` + } + err := mapForm(&s, map[string][]string{"field": {""}}) + require.NoError(t, err) + assert.Equal(t, "DefVal", s.F) +} + func TestMapFormWithTag(t *testing.T) { var s struct { F int `externalTag:"field"` From f2c861a24f204f53dd6e6755b6d4efece7e373ea Mon Sep 17 00:00:00 2001 From: demouth <1133178+demouth@users.noreply.github.com> Date: Sun, 15 Sep 2024 09:54:23 +0900 Subject: [PATCH 330/346] docs: fix route group example code (#4020) --- docs/doc.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/doc.md b/docs/doc.md index b76011f2ab..875dd619a0 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -339,16 +339,16 @@ func main() { router := gin.Default() // Simple group: v1 - v1 := router.Group("/v1") { + v1 := router.Group("/v1") v1.POST("/login", loginEndpoint) v1.POST("/submit", submitEndpoint) v1.POST("/read", readEndpoint) } // Simple group: v2 - v2 := router.Group("/v2") { + v2 := router.Group("/v2") v2.POST("/login", loginEndpoint) v2.POST("/submit", submitEndpoint) v2.POST("/read", readEndpoint) From 9d7c0e9e1a301f417df9dc89a8cadc3bf9063db2 Mon Sep 17 00:00:00 2001 From: CC11001100 Date: Sun, 15 Sep 2024 08:58:59 +0800 Subject: [PATCH 331/346] feat(context): GetXxx added support for more go native types (#3633) --- context.go | 144 ++++++++++++++++++++++++++++++++++++++++++++- context_test.go | 152 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index baa4b0f9c9..1f3565158f 100644 --- a/context.go +++ b/context.go @@ -315,7 +315,31 @@ func (c *Context) GetInt(key string) (i int) { return } -// GetInt64 returns the value associated with the key as an integer. +// GetInt8 returns the value associated with the key as an integer 8. +func (c *Context) GetInt8(key string) (i8 int8) { + if val, ok := c.Get(key); ok && val != nil { + i8, _ = val.(int8) + } + return +} + +// GetInt16 returns the value associated with the key as an integer 16. +func (c *Context) GetInt16(key string) (i16 int16) { + if val, ok := c.Get(key); ok && val != nil { + i16, _ = val.(int16) + } + return +} + +// GetInt32 returns the value associated with the key as an integer 32. +func (c *Context) GetInt32(key string) (i32 int32) { + if val, ok := c.Get(key); ok && val != nil { + i32, _ = val.(int32) + } + return +} + +// GetInt64 returns the value associated with the key as an integer 64. func (c *Context) GetInt64(key string) (i64 int64) { if val, ok := c.Get(key); ok && val != nil { i64, _ = val.(int64) @@ -331,7 +355,31 @@ func (c *Context) GetUint(key string) (ui uint) { return } -// GetUint64 returns the value associated with the key as an unsigned integer. +// GetUint8 returns the value associated with the key as an unsigned integer 8. +func (c *Context) GetUint8(key string) (ui8 uint8) { + if val, ok := c.Get(key); ok && val != nil { + ui8, _ = val.(uint8) + } + return +} + +// GetUint16 returns the value associated with the key as an unsigned integer 16. +func (c *Context) GetUint16(key string) (ui16 uint16) { + if val, ok := c.Get(key); ok && val != nil { + ui16, _ = val.(uint16) + } + return +} + +// GetUint32 returns the value associated with the key as an unsigned integer 32. +func (c *Context) GetUint32(key string) (ui32 uint32) { + if val, ok := c.Get(key); ok && val != nil { + ui32, _ = val.(uint32) + } + return +} + +// GetUint64 returns the value associated with the key as an unsigned integer 64. func (c *Context) GetUint64(key string) (ui64 uint64) { if val, ok := c.Get(key); ok && val != nil { ui64, _ = val.(uint64) @@ -339,6 +387,14 @@ func (c *Context) GetUint64(key string) (ui64 uint64) { return } +// GetFloat32 returns the value associated with the key as a float32. +func (c *Context) GetFloat32(key string) (f32 float32) { + if val, ok := c.Get(key); ok && val != nil { + f32, _ = val.(float32) + } + return +} + // GetFloat64 returns the value associated with the key as a float64. func (c *Context) GetFloat64(key string) (f64 float64) { if val, ok := c.Get(key); ok && val != nil { @@ -363,6 +419,90 @@ func (c *Context) GetDuration(key string) (d time.Duration) { return } +func (c *Context) GetIntSlice(key string) (is []int) { + if val, ok := c.Get(key); ok && val != nil { + is, _ = val.([]int) + } + return +} + +func (c *Context) GetInt8Slice(key string) (i8s []int8) { + if val, ok := c.Get(key); ok && val != nil { + i8s, _ = val.([]int8) + } + return +} + +func (c *Context) GetInt16Slice(key string) (i16s []int16) { + if val, ok := c.Get(key); ok && val != nil { + i16s, _ = val.([]int16) + } + return +} + +func (c *Context) GetInt32Slice(key string) (i32s []int32) { + if val, ok := c.Get(key); ok && val != nil { + i32s, _ = val.([]int32) + } + return +} + +func (c *Context) GetInt64Slice(key string) (i64s []int64) { + if val, ok := c.Get(key); ok && val != nil { + i64s, _ = val.([]int64) + } + return +} + +func (c *Context) GetUintSlice(key string) (uis []uint) { + if val, ok := c.Get(key); ok && val != nil { + uis, _ = val.([]uint) + } + return +} + +func (c *Context) GetUint8Slice(key string) (ui8s []uint8) { + if val, ok := c.Get(key); ok && val != nil { + ui8s, _ = val.([]uint8) + } + return +} + +func (c *Context) GetUint16Slice(key string) (ui16s []uint16) { + if val, ok := c.Get(key); ok && val != nil { + ui16s, _ = val.([]uint16) + } + return +} + +func (c *Context) GetUint32Slice(key string) (ui32s []uint32) { + if val, ok := c.Get(key); ok && val != nil { + ui32s, _ = val.([]uint32) + } + return +} + +func (c *Context) GetUint64Slice(key string) (ui64s []uint64) { + if val, ok := c.Get(key); ok && val != nil { + ui64s, _ = val.([]uint64) + } + return +} + +func (c *Context) GetFloat32Slice(key string) (f32s []float32) { + if val, ok := c.Get(key); ok && val != nil { + f32s, _ = val.([]float32) + } + return +} + +func (c *Context) GetFloat64Slice(key string) (f64s []float64) { + if val, ok := c.Get(key); ok && val != nil { + f64s, _ = val.([]float64) + } + return +} + // GetStringSlice returns the value associated with the key as a slice of strings. func (c *Context) GetStringSlice(key string) (ss []string) { if val, ok := c.Get(key); ok && val != nil { diff --git a/context_test.go b/context_test.go index 66190b302e..211fbb1b3d 100644 --- a/context_test.go +++ b/context_test.go @@ -252,6 +252,30 @@ func TestContextGetInt(t *testing.T) { assert.Equal(t, 1, c.GetInt("int")) } +func TestContextGetInt8(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "int8" + value := int8(0x7F) + c.Set(key, value) + assert.Equal(t, value, c.GetInt8(key)) +} + +func TestContextGetInt16(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "int16" + value := int16(0x7FFF) + c.Set(key, value) + assert.Equal(t, value, c.GetInt16(key)) +} + +func TestContextGetInt32(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "int32" + value := int32(0x7FFFFFFF) + c.Set(key, value) + assert.Equal(t, value, c.GetInt32(key)) +} + func TestContextGetInt64(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Set("int64", int64(42424242424242)) @@ -264,12 +288,44 @@ func TestContextGetUint(t *testing.T) { assert.Equal(t, uint(1), c.GetUint("uint")) } +func TestContextGetUint8(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "uint8" + value := uint8(0xFF) + c.Set(key, value) + assert.Equal(t, value, c.GetUint8(key)) +} + +func TestContextGetUint16(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "uint16" + value := uint16(0xFFFF) + c.Set(key, value) + assert.Equal(t, value, c.GetUint16(key)) +} + +func TestContextGetUint32(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "uint32" + value := uint32(0xFFFFFFFF) + c.Set(key, value) + assert.Equal(t, value, c.GetUint32(key)) +} + func TestContextGetUint64(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Set("uint64", uint64(18446744073709551615)) assert.Equal(t, uint64(18446744073709551615), c.GetUint64("uint64")) } +func TestContextGetFloat32(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "float32" + value := float32(3.14) + c.Set(key, value) + assert.Equal(t, value, c.GetFloat32(key)) +} + func TestContextGetFloat64(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Set("float64", 4.2) @@ -289,6 +345,102 @@ func TestContextGetDuration(t *testing.T) { assert.Equal(t, time.Second, c.GetDuration("duration")) } +func TestContextGetIntSlice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "int-slice" + value := []int{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetIntSlice(key)) +} + +func TestContextGetInt8Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "int8-slice" + value := []int8{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetInt8Slice(key)) +} + +func TestContextGetInt16Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "int16-slice" + value := []int16{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetInt16Slice(key)) +} + +func TestContextGetInt32Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "int32-slice" + value := []int32{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetInt32Slice(key)) +} + +func TestContextGetInt64Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "int64-slice" + value := []int64{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetInt64Slice(key)) +} + +func TestContextGetUintSlice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "uint-slice" + value := []uint{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetUintSlice(key)) +} + +func TestContextGetUint8Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "uint8-slice" + value := []uint8{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetUint8Slice(key)) +} + +func TestContextGetUint16Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "uint16-slice" + value := []uint16{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetUint16Slice(key)) +} + +func TestContextGetUint32Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "uint32-slice" + value := []uint32{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetUint32Slice(key)) +} + +func TestContextGetUint64Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "uint64-slice" + value := []uint64{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetUint64Slice(key)) +} + +func TestContextGetFloat32Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "float32-slice" + value := []float32{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetFloat32Slice(key)) +} + +func TestContextGetFloat64Slice(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + key := "float64-slice" + value := []float64{1, 2} + c.Set(key, value) + assert.Equal(t, value, c.GetFloat64Slice(key)) +} + func TestContextGetStringSlice(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Set("slice", []string{"foo"}) From f05f966a0824b1d302ee556183e2579c91954266 Mon Sep 17 00:00:00 2001 From: takanuva15 <6986426+takanuva15@users.noreply.github.com> Date: Sat, 21 Sep 2024 11:24:18 -0400 Subject: [PATCH 332/346] feat(form): Support default values for collections in form binding (#4048) --- binding/form_mapping.go | 20 +++++++++++ binding/form_mapping_test.go | 66 ++++++++++++++++++++++++++++++++++++ docs/doc.md | 48 ++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index a84536f722..f5f6f3ae93 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -159,6 +159,14 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter if k, v := head(opt, "="); k == "default" { setOpt.isDefaultExists = true setOpt.defaultValue = v + + // convert semicolon-separated default values to csv-separated values for processing in setByForm + if field.Type.Kind() == reflect.Slice || field.Type.Kind() == reflect.Array { + cfTag := field.Tag.Get("collection_format") + if cfTag == "" || cfTag == "multi" || cfTag == "csv" { + setOpt.defaultValue = strings.ReplaceAll(v, ";", ",") + } + } } } @@ -224,6 +232,12 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][ case reflect.Slice: if !ok { vs = []string{opt.defaultValue} + + // pre-process the default value for multi if present + cfTag := field.Tag.Get("collection_format") + if cfTag == "" || cfTag == "multi" { + vs = strings.Split(opt.defaultValue, ",") + } } if ok, err = trySetCustom(vs[0], value); ok { @@ -238,6 +252,12 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][ case reflect.Array: if !ok { vs = []string{opt.defaultValue} + + // pre-process the default value for multi if present + cfTag := field.Tag.Get("collection_format") + if cfTag == "" || cfTag == "multi" { + vs = strings.Split(opt.defaultValue, ",") + } } if ok, err = trySetCustom(vs[0], value); ok { diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 8cf7465173..810315bb1a 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -323,6 +323,72 @@ func TestMappingCollectionFormat(t *testing.T) { assert.Equal(t, [2]int{1, 2}, s.ArrayPipes) } +func TestMappingCollectionFormatInvalid(t *testing.T) { + var s struct { + SliceCsv []int `form:"slice_csv" collection_format:"xxx"` + } + err := mappingByPtr(&s, formSource{ + "slice_csv": {"1,2"}, + }, "form") + require.Error(t, err) + + var s2 struct { + ArrayCsv [2]int `form:"array_csv" collection_format:"xxx"` + } + err = mappingByPtr(&s2, formSource{ + "array_csv": {"1,2"}, + }, "form") + require.Error(t, err) +} + +func TestMappingMultipleDefaultWithCollectionFormat(t *testing.T) { + var s struct { + SliceMulti []int `form:",default=1;2;3" collection_format:"multi"` + SliceCsv []int `form:",default=1;2;3" collection_format:"csv"` + SliceSsv []int `form:",default=1 2 3" collection_format:"ssv"` + SliceTsv []int `form:",default=1\t2\t3" collection_format:"tsv"` + SlicePipes []int `form:",default=1|2|3" collection_format:"pipes"` + ArrayMulti [2]int `form:",default=1;2" collection_format:"multi"` + ArrayCsv [2]int `form:",default=1;2" collection_format:"csv"` + ArraySsv [2]int `form:",default=1 2" collection_format:"ssv"` + ArrayTsv [2]int `form:",default=1\t2" collection_format:"tsv"` + ArrayPipes [2]int `form:",default=1|2" collection_format:"pipes"` + SliceStringMulti []string `form:",default=1;2;3" collection_format:"multi"` + SliceStringCsv []string `form:",default=1;2;3" collection_format:"csv"` + SliceStringSsv []string `form:",default=1 2 3" collection_format:"ssv"` + SliceStringTsv []string `form:",default=1\t2\t3" collection_format:"tsv"` + SliceStringPipes []string `form:",default=1|2|3" collection_format:"pipes"` + ArrayStringMulti [2]string `form:",default=1;2" collection_format:"multi"` + ArrayStringCsv [2]string `form:",default=1;2" collection_format:"csv"` + ArrayStringSsv [2]string `form:",default=1 2" collection_format:"ssv"` + ArrayStringTsv [2]string `form:",default=1\t2" collection_format:"tsv"` + ArrayStringPipes [2]string `form:",default=1|2" collection_format:"pipes"` + } + err := mappingByPtr(&s, formSource{}, "form") + require.NoError(t, err) + + assert.Equal(t, []int{1, 2, 3}, s.SliceMulti) + assert.Equal(t, []int{1, 2, 3}, s.SliceCsv) + assert.Equal(t, []int{1, 2, 3}, s.SliceSsv) + assert.Equal(t, []int{1, 2, 3}, s.SliceTsv) + assert.Equal(t, []int{1, 2, 3}, s.SlicePipes) + assert.Equal(t, [2]int{1, 2}, s.ArrayMulti) + assert.Equal(t, [2]int{1, 2}, s.ArrayCsv) + assert.Equal(t, [2]int{1, 2}, s.ArraySsv) + assert.Equal(t, [2]int{1, 2}, s.ArrayTsv) + assert.Equal(t, [2]int{1, 2}, s.ArrayPipes) + assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringMulti) + assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringCsv) + assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringSsv) + assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringTsv) + assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringPipes) + assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringMulti) + assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringCsv) + assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringSsv) + assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringTsv) + assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringPipes) +} + func TestMappingStructField(t *testing.T) { var s struct { J struct { diff --git a/docs/doc.md b/docs/doc.md index 875dd619a0..8cb53cc2d3 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -26,6 +26,7 @@ - [Custom Validators](#custom-validators) - [Only Bind Query String](#only-bind-query-string) - [Bind Query String or Post Data](#bind-query-string-or-post-data) + - [Bind default value if none provided](#bind-default-value-if-none-provided) - [Collection format for arrays](#collection-format-for-arrays) - [Bind Uri](#bind-uri) - [Bind custom unmarshaler](#bind-custom-unmarshaler) @@ -862,6 +863,53 @@ Test it with: curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033" ``` + +### Bind default value if none provided + +If the server should bind a default value to a field when the client does not provide one, specify the default value using the `default` key within the `form` tag: + +``` +package main + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type Person struct { + Name string `form:"name,default=William"` + Age int `form:"age,default=10"` + Friends []string `form:"friends,default=Will;Bill"` + Addresses [2]string `form:"addresses,default=foo bar" collection_format:"ssv"` + LapTimes []int `form:"lap_times,default=1;2;3" collection_format:"csv"` +} + +func main() { + g := gin.Default() + g.POST("/person", func(c *gin.Context) { + var req Person + if err := c.ShouldBindQuery(&req); err != nil { + c.JSON(http.StatusBadRequest, err) + return + } + c.JSON(http.StatusOK, req) + }) + _ = g.Run("localhost:8080") +} +``` + +``` +curl -X POST http://localhost:8080/person +{"Name":"William","Age":10,"Friends":["Will","Bill"],"Colors":["red","blue"],"LapTimes":[1,2,3]} +``` + +NOTE: For default [collection values](#collection-format-for-arrays), the following rules apply: +- Since commas are used to delimit tag options, they are not supported within a default value and will result in undefined behavior +- For the collection formats "multi" and "csv", a semicolon should be used in place of a comma to delimited default values +- Since semicolons are used to delimit default values for "multi" and "csv", they are not supported within a default value for "multi" and "csv" + + #### Collection format for arrays | Format | Description | Example | From ad740d508f3e98b53ecafda35b66e6a32f6758ac Mon Sep 17 00:00:00 2001 From: wangjingcun Date: Fri, 25 Oct 2024 09:07:03 +0800 Subject: [PATCH 333/346] docs(context): fix some function names in comment (#4079) --- context_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/context_test.go b/context_test.go index 211fbb1b3d..dda5997879 100644 --- a/context_test.go +++ b/context_test.go @@ -1153,7 +1153,7 @@ func TestContextRenderNoContentHTMLString(t *testing.T) { assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } -// TestContextData tests that the response can be written from `bytestring` +// TestContextRenderData tests that the response can be written from `bytestring` // with specified MIME type func TestContextRenderData(t *testing.T) { w := httptest.NewRecorder() @@ -1550,7 +1550,7 @@ func TestContextIsAborted(t *testing.T) { assert.True(t, c.IsAborted()) } -// TestContextData tests that the response can be written from `bytestring` +// TestContextAbortWithStatus tests that the response can be written from `bytestring` // with specified MIME type func TestContextAbortWithStatus(t *testing.T) { w := httptest.NewRecorder() From b080116a7f5c71f023e1059ebb9e99a799938909 Mon Sep 17 00:00:00 2001 From: Enzo Lanzellotti <102574758+YlanzinhoY@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:08:11 -0300 Subject: [PATCH 334/346] docs(readme): add Portuguese documentation. (#4078) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index faeb4952aa..0464107c3a 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ The documentation is also available on [gin-gonic.com](https://gin-gonic.com) in - [한국어](https://gin-gonic.com/ko-kr/docs/) - [Turkish](https://gin-gonic.com/tr/docs/) - [Persian](https://gin-gonic.com/fa/docs/) +- [Português](https://gin-gonic.com/pt/docs/) ### Articles From 299c6f30e3df4c5a257c517a91f421ff3ea63a8e Mon Sep 17 00:00:00 2001 From: tsukasa-ino Date: Fri, 25 Oct 2024 10:16:40 +0900 Subject: [PATCH 335/346] docs: trimmed some white spaces (#4070) --- docs/doc.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/doc.md b/docs/doc.md index 8cb53cc2d3..a463e820ea 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -172,7 +172,7 @@ func main() { router := gin.Default() // Query string parameters are parsed using the existing underlying request object. - // The request responds to an url matching: /welcome?firstname=Jane&lastname=Doe + // The request responds to an url matching: /welcome?firstname=Jane&lastname=Doe router.GET("/welcome", func(c *gin.Context) { firstname := c.DefaultQuery("firstname", "Guest") lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") @@ -516,19 +516,19 @@ Sample Output ```go func main() { router := gin.New() - + // skip logging for desired paths by setting SkipPaths in LoggerConfig loggerConfig := gin.LoggerConfig{SkipPaths: []string{"/metrics"}} - + // skip logging based on your logic by setting Skip func in LoggerConfig loggerConfig.Skip = func(c *gin.Context) bool { // as an example skip non server side errors return c.Writer.Status() < http.StatusInternalServerError } - + router.Use(gin.LoggerWithConfig(loggerConfig)) router.Use(gin.Recovery()) - + // skipped router.GET("/metrics", func(c *gin.Context) { c.Status(http.StatusNotImplemented) @@ -543,7 +543,7 @@ func main() { router.GET("/data", func(c *gin.Context) { c.Status(http.StatusNotImplemented) }) - + router.Run(":8080") } @@ -615,7 +615,7 @@ You can also specify that specific fields are required. If a field is decorated ```go // Binding from JSON type Login struct { - User string `form:"user" json:"user" xml:"user" binding:"required"` + User string `form:"user" json:"user" xml:"user" binding:"required"` Password string `form:"password" json:"password" xml:"password" binding:"required"` } @@ -1252,7 +1252,7 @@ func main() { #### JSONP -Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists. +Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists. ```go func main() { @@ -1301,7 +1301,7 @@ func main() { #### PureJSON -Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead. +Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead. This feature is unavailable in Go 1.6 and lower. ```go @@ -1336,7 +1336,7 @@ func main() { router.StaticFS("/more_static", http.Dir("my_file_system")) router.StaticFile("/favicon.ico", "./resources/favicon.ico") router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system")) - + // Listen and serve on 0.0.0.0:8080 router.Run(":8080") } @@ -2320,7 +2320,7 @@ or network CIDRs from where clients which their request headers related to clien IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or IPv6 CIDRs. -**Attention:** Gin trust all proxies by default if you don't specify a trusted +**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 @@ -2349,7 +2349,7 @@ func main() { ``` **Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform` -to skip TrustedProxies check, it has a higher priority than TrustedProxies. +to skip TrustedProxies check, it has a higher priority than TrustedProxies. Look at the example below: ```go From 647311aba203dd7262b24f973503e7689e00389d Mon Sep 17 00:00:00 2001 From: Xinyu Kuo Date: Fri, 25 Oct 2024 09:33:31 +0800 Subject: [PATCH 336/346] refactor(context): refactor context handling and improve test robustness (#4066) Use assert.InDelta for float comparison with tolerance in TestContextGetFloat32 Remove unnecessary blank line in TestContextInitQueryCache Replace anonymous struct with named contextKey type in TestContextWithFallbackValueFromRequestContext Update context key handling in TestContextWithFallbackValueFromRequestContext to use contextKey type --- context.go | 2 +- context_test.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index 1f3565158f..77232f9051 100644 --- a/context.go +++ b/context.go @@ -951,7 +951,7 @@ func (c *Context) ShouldBindBodyWithTOML(obj any) error { return c.ShouldBindBodyWith(obj, binding.TOML) } -// ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON). +// ShouldBindBodyWithPlain is a shortcut for c.ShouldBindBodyWith(obj, binding.Plain). func (c *Context) ShouldBindBodyWithPlain(obj any) error { return c.ShouldBindBodyWith(obj, binding.Plain) } diff --git a/context_test.go b/context_test.go index dda5997879..62a1e14f98 100644 --- a/context_test.go +++ b/context_test.go @@ -323,7 +323,7 @@ func TestContextGetFloat32(t *testing.T) { key := "float32" value := float32(3.14) c.Set(key, value) - assert.Equal(t, value, c.GetFloat32(key)) + assert.InDelta(t, value, c.GetFloat32(key), 0.01) } func TestContextGetFloat64(t *testing.T) { @@ -2857,7 +2857,8 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { { name: "c with struct context key", getContextAndKey: func() (*Context, any) { - var key struct{} + type KeyStruct struct{} // https://staticcheck.dev/docs/checks/#SA1029 + var key KeyStruct c, _ := CreateTestContext(httptest.NewRecorder()) // enable ContextWithFallback feature flag c.engine.ContextWithFallback = true From 9d11234efec1e5517b2887a6e7dfbc9c017bc52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Karpi=C5=84ski?= Date: Sat, 26 Oct 2024 02:26:25 +0200 Subject: [PATCH 337/346] docs(gin): Replace broken link to documentation with valid (#4064) --- gin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 48cc15c985..e17596aa41 100644 --- a/gin.go +++ b/gin.go @@ -598,7 +598,7 @@ func (engine *Engine) RunQUIC(addr, certFile, keyFile string) (err error) { if engine.isUnsafeTrustedProxies() { debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + - "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") } err = http3.ListenAndServeQUIC(addr, certFile, keyFile, engine.Handler()) From ea53388e6ee4a6a0a1647b390c56eeed780e7e56 Mon Sep 17 00:00:00 2001 From: Xinyu Kuo Date: Sat, 26 Oct 2024 08:28:59 +0800 Subject: [PATCH 338/346] fix(tree): Keep panic infos consistent when wildcard type build faild (#4077) --- .github/workflows/gin.yml | 2 +- tree.go | 2 +- tree_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 947abf9c90..74983c5006 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -26,7 +26,7 @@ jobs: - name: Setup golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: v1.58.1 + version: v1.61.0 args: --verbose test: needs: lint diff --git a/tree.go b/tree.go index b0a5f98222..0d3e5a8c12 100644 --- a/tree.go +++ b/tree.go @@ -369,7 +369,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) // currently fixed width 1 for '/' i-- - if path[i] != '/' { + if i < 0 || path[i] != '/' { panic("no / before catch-all in path '" + fullPath + "'") } diff --git a/tree_test.go b/tree_test.go index 3aa3a59444..74eb61049b 100644 --- a/tree_test.go +++ b/tree_test.go @@ -993,3 +993,28 @@ func TestTreeInvalidEscape(t *testing.T) { } } } + +func TestWildcardInvalidSlash(t *testing.T) { + const panicMsgPrefix = "no / before catch-all in path" + + routes := map[string]bool{ + "/foo/bar": true, + "/foo/x*zy": false, + "/foo/b*r": false, + } + + for route, valid := range routes { + tree := &node{} + recv := catchPanic(func() { + tree.addRoute(route, nil) + }) + + if recv == nil != valid { + t.Fatalf("%s should be %t but got %v", route, valid, recv) + } + + if rs, ok := recv.(string); recv != nil && (!ok || !strings.HasPrefix(rs, panicMsgPrefix)) { + t.Fatalf(`"Expected panic "%s" for route '%s', got "%v"`, panicMsgPrefix, route, recv) + } + } +} From c8a3adc65703d8958265c07689662e54f037038c Mon Sep 17 00:00:00 2001 From: Konovalov Maxim <43151027+KaymeKaydex@users.noreply.github.com> Date: Tue, 29 Oct 2024 18:24:53 +0300 Subject: [PATCH 339/346] refactor(context): simplify "GetType()" functions (#4080) This PR introduces a generic function, getTyped[T any], to simplify value retrieval in the Context struct. It replaces repetitive type assertions in the GetString GetBool etc. methods. Co-authored-by: Maksim Konovalov --- context.go | 177 +++++++++++++++-------------------------------------- 1 file changed, 50 insertions(+), 127 deletions(-) diff --git a/context.go b/context.go index 77232f9051..1e8c8e27f3 100644 --- a/context.go +++ b/context.go @@ -291,248 +291,171 @@ func (c *Context) MustGet(key string) any { panic("Key \"" + key + "\" does not exist") } -// GetString returns the value associated with the key as a string. -func (c *Context) GetString(key string) (s string) { +func getTyped[T any](c *Context, key string) (res T) { if val, ok := c.Get(key); ok && val != nil { - s, _ = val.(string) + res, _ = val.(T) } return } +// GetString returns the value associated with the key as a string. +func (c *Context) GetString(key string) (s string) { + return getTyped[string](c, key) +} + // GetBool returns the value associated with the key as a boolean. func (c *Context) GetBool(key string) (b bool) { - if val, ok := c.Get(key); ok && val != nil { - b, _ = val.(bool) - } - return + return getTyped[bool](c, key) } // GetInt returns the value associated with the key as an integer. func (c *Context) GetInt(key string) (i int) { - if val, ok := c.Get(key); ok && val != nil { - i, _ = val.(int) - } - return + return getTyped[int](c, key) } // GetInt8 returns the value associated with the key as an integer 8. func (c *Context) GetInt8(key string) (i8 int8) { - if val, ok := c.Get(key); ok && val != nil { - i8, _ = val.(int8) - } - return + return getTyped[int8](c, key) } // GetInt16 returns the value associated with the key as an integer 16. func (c *Context) GetInt16(key string) (i16 int16) { - if val, ok := c.Get(key); ok && val != nil { - i16, _ = val.(int16) - } - return + return getTyped[int16](c, key) } // GetInt32 returns the value associated with the key as an integer 32. func (c *Context) GetInt32(key string) (i32 int32) { - if val, ok := c.Get(key); ok && val != nil { - i32, _ = val.(int32) - } - return + return getTyped[int32](c, key) } // GetInt64 returns the value associated with the key as an integer 64. func (c *Context) GetInt64(key string) (i64 int64) { - if val, ok := c.Get(key); ok && val != nil { - i64, _ = val.(int64) - } - return + return getTyped[int64](c, key) } // GetUint returns the value associated with the key as an unsigned integer. func (c *Context) GetUint(key string) (ui uint) { - if val, ok := c.Get(key); ok && val != nil { - ui, _ = val.(uint) - } - return + return getTyped[uint](c, key) } // GetUint8 returns the value associated with the key as an unsigned integer 8. func (c *Context) GetUint8(key string) (ui8 uint8) { - if val, ok := c.Get(key); ok && val != nil { - ui8, _ = val.(uint8) - } - return + return getTyped[uint8](c, key) } // GetUint16 returns the value associated with the key as an unsigned integer 16. func (c *Context) GetUint16(key string) (ui16 uint16) { - if val, ok := c.Get(key); ok && val != nil { - ui16, _ = val.(uint16) - } - return + return getTyped[uint16](c, key) } // GetUint32 returns the value associated with the key as an unsigned integer 32. func (c *Context) GetUint32(key string) (ui32 uint32) { - if val, ok := c.Get(key); ok && val != nil { - ui32, _ = val.(uint32) - } - return + return getTyped[uint32](c, key) } // GetUint64 returns the value associated with the key as an unsigned integer 64. func (c *Context) GetUint64(key string) (ui64 uint64) { - if val, ok := c.Get(key); ok && val != nil { - ui64, _ = val.(uint64) - } - return + return getTyped[uint64](c, key) } // GetFloat32 returns the value associated with the key as a float32. func (c *Context) GetFloat32(key string) (f32 float32) { - if val, ok := c.Get(key); ok && val != nil { - f32, _ = val.(float32) - } - return + return getTyped[float32](c, key) } // GetFloat64 returns the value associated with the key as a float64. func (c *Context) GetFloat64(key string) (f64 float64) { - if val, ok := c.Get(key); ok && val != nil { - f64, _ = val.(float64) - } - return + return getTyped[float64](c, key) } // GetTime returns the value associated with the key as time. func (c *Context) GetTime(key string) (t time.Time) { - if val, ok := c.Get(key); ok && val != nil { - t, _ = val.(time.Time) - } - return + return getTyped[time.Time](c, key) } // GetDuration returns the value associated with the key as a duration. func (c *Context) GetDuration(key string) (d time.Duration) { - if val, ok := c.Get(key); ok && val != nil { - d, _ = val.(time.Duration) - } - return + return getTyped[time.Duration](c, key) } +// GetIntSlice returns the value associated with the key as a slice of integers. func (c *Context) GetIntSlice(key string) (is []int) { - if val, ok := c.Get(key); ok && val != nil { - is, _ = val.([]int) - } - return + return getTyped[[]int](c, key) } +// GetInt8Slice returns the value associated with the key as a slice of int8 integers. func (c *Context) GetInt8Slice(key string) (i8s []int8) { - if val, ok := c.Get(key); ok && val != nil { - i8s, _ = val.([]int8) - } - return + return getTyped[[]int8](c, key) } +// GetInt16Slice returns the value associated with the key as a slice of int16 integers. func (c *Context) GetInt16Slice(key string) (i16s []int16) { - if val, ok := c.Get(key); ok && val != nil { - i16s, _ = val.([]int16) - } - return + return getTyped[[]int16](c, key) } +// GetInt32Slice returns the value associated with the key as a slice of int32 integers. func (c *Context) GetInt32Slice(key string) (i32s []int32) { - if val, ok := c.Get(key); ok && val != nil { - i32s, _ = val.([]int32) - } - return + return getTyped[[]int32](c, key) } +// GetInt64Slice returns the value associated with the key as a slice of int64 integers. func (c *Context) GetInt64Slice(key string) (i64s []int64) { - if val, ok := c.Get(key); ok && val != nil { - i64s, _ = val.([]int64) - } - return + return getTyped[[]int64](c, key) } +// GetUintSlice returns the value associated with the key as a slice of unsigned integers. func (c *Context) GetUintSlice(key string) (uis []uint) { - if val, ok := c.Get(key); ok && val != nil { - uis, _ = val.([]uint) - } - return + return getTyped[[]uint](c, key) } +// GetUint8Slice returns the value associated with the key as a slice of uint8 integers. func (c *Context) GetUint8Slice(key string) (ui8s []uint8) { - if val, ok := c.Get(key); ok && val != nil { - ui8s, _ = val.([]uint8) - } - return + return getTyped[[]uint8](c, key) } +// GetUint16Slice returns the value associated with the key as a slice of uint16 integers. func (c *Context) GetUint16Slice(key string) (ui16s []uint16) { - if val, ok := c.Get(key); ok && val != nil { - ui16s, _ = val.([]uint16) - } - return + return getTyped[[]uint16](c, key) } +// GetUint32Slice returns the value associated with the key as a slice of uint32 integers. func (c *Context) GetUint32Slice(key string) (ui32s []uint32) { - if val, ok := c.Get(key); ok && val != nil { - ui32s, _ = val.([]uint32) - } - return + return getTyped[[]uint32](c, key) } +// GetUint64Slice returns the value associated with the key as a slice of uint64 integers. func (c *Context) GetUint64Slice(key string) (ui64s []uint64) { - if val, ok := c.Get(key); ok && val != nil { - ui64s, _ = val.([]uint64) - } - return + return getTyped[[]uint64](c, key) } +// GetFloat32Slice returns the value associated with the key as a slice of float32 numbers. func (c *Context) GetFloat32Slice(key string) (f32s []float32) { - if val, ok := c.Get(key); ok && val != nil { - f32s, _ = val.([]float32) - } - return + return getTyped[[]float32](c, key) } +// GetFloat64Slice returns the value associated with the key as a slice of float64 numbers. func (c *Context) GetFloat64Slice(key string) (f64s []float64) { - if val, ok := c.Get(key); ok && val != nil { - f64s, _ = val.([]float64) - } - return + return getTyped[[]float64](c, key) } // GetStringSlice returns the value associated with the key as a slice of strings. func (c *Context) GetStringSlice(key string) (ss []string) { - if val, ok := c.Get(key); ok && val != nil { - ss, _ = val.([]string) - } - return + return getTyped[[]string](c, key) } // GetStringMap returns the value associated with the key as a map of interfaces. func (c *Context) GetStringMap(key string) (sm map[string]any) { - if val, ok := c.Get(key); ok && val != nil { - sm, _ = val.(map[string]any) - } - return + return getTyped[map[string]any](c, key) } // GetStringMapString returns the value associated with the key as a map of strings. func (c *Context) GetStringMapString(key string) (sms map[string]string) { - if val, ok := c.Get(key); ok && val != nil { - sms, _ = val.(map[string]string) - } - return + return getTyped[map[string]string](c, key) } // GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) { - if val, ok := c.Get(key); ok && val != nil { - smss, _ = val.(map[string][]string) - } - return + return getTyped[map[string][]string](c, key) } /************************************/ From f875d8728306c2c2c6f504900ab08cd1d8c47f12 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 15 Nov 2024 23:49:08 +0800 Subject: [PATCH 340/346] chore(context): test context initialization and handler logic (#4087) * enhance code imported by #3413 if it needs to check if the handler is nil, tie c.index shall always ++ * test: refactor test context initialization and handler logic - Remove an empty line in `TestContextInitQueryCache` - Add `TestContextNext` function with tests for `Next` method behavior with no handlers, one handler, and multiple handlers Signed-off-by: Bo-Yi Wu --------- Signed-off-by: Bo-Yi Wu Co-authored-by: zjj --- context.go | 5 ++--- context_test.go | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/context.go b/context.go index 1e8c8e27f3..cab14529b4 100644 --- a/context.go +++ b/context.go @@ -186,10 +186,9 @@ func (c *Context) FullPath() string { func (c *Context) Next() { c.index++ for c.index < int8(len(c.handlers)) { - if c.handlers[c.index] == nil { - continue + if c.handlers[c.index] != nil { + c.handlers[c.index](c) } - c.handlers[c.index](c) c.index++ } } diff --git a/context_test.go b/context_test.go index 62a1e14f98..6921deabe6 100644 --- a/context_test.go +++ b/context_test.go @@ -610,7 +610,6 @@ func TestContextInitQueryCache(t *testing.T) { assert.Equal(t, test.expectedQueryCache, test.testContext.queryCache) }) } - } func TestContextDefaultQueryOnEmptyRequest(t *testing.T) { @@ -3038,3 +3037,47 @@ func TestInterceptedHeader(t *testing.T) { assert.Equal(t, "", w.Result().Header.Get("X-Test")) assert.Equal(t, "present", w.Result().Header.Get("X-Test-2")) } + +func TestContextNext(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + + // Test with no handlers + c.Next() + assert.Equal(t, int8(0), c.index) + + // Test with one handler + c.index = -1 + c.handlers = HandlersChain{func(c *Context) { + c.Set("key", "value") + }} + c.Next() + assert.Equal(t, int8(1), c.index) + value, exists := c.Get("key") + assert.True(t, exists) + assert.Equal(t, "value", value) + + // Test with multiple handlers + c.handlers = HandlersChain{ + func(c *Context) { + c.Set("key1", "value1") + c.Next() + c.Set("key2", "value2") + }, + nil, + func(c *Context) { + c.Set("key3", "value3") + }, + } + c.index = -1 + c.Next() + assert.Equal(t, int8(4), c.index) + value, exists = c.Get("key1") + assert.True(t, exists) + assert.Equal(t, "value1", value) + value, exists = c.Get("key2") + assert.True(t, exists) + assert.Equal(t, "value2", value) + value, exists = c.Get("key3") + assert.True(t, exists) + assert.Equal(t, "value3", value) +} From 02c1144f312eaf18767475a578bc421ddbcc4b82 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Fri, 15 Nov 2024 16:51:12 +0100 Subject: [PATCH 341/346] ci(lint): enable perfsprint linter (#4090) Signed-off-by: Matthieu MOREL --- .golangci.yml | 8 ++++++++ binding/form_mapping_test.go | 8 ++++---- context_test.go | 5 +++-- gin_test.go | 20 ++++++++++---------- githubapi_test.go | 5 +++-- recovery_test.go | 3 +-- routes_test.go | 4 ++-- 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 8d58c989be..c3ae727523 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -16,6 +16,7 @@ linters: - nakedret - nilerr - nolintlint + - perfsprint - revive - testifylint - wastedassign @@ -34,6 +35,13 @@ linters-settings: - G112 - G201 - G203 + perfsprint: + err-error: true + errorf: true + fiximports: true + int-conversion: true + sprintf1: true + strconcat: true testifylint: enable-all: true diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 810315bb1a..1277fd5ffd 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -6,7 +6,7 @@ package binding import ( "encoding/hex" - "fmt" + "errors" "mime/multipart" "reflect" "strconv" @@ -494,7 +494,7 @@ type customUnmarshalParamType struct { func (f *customUnmarshalParamType) UnmarshalParam(param string) error { parts := strings.Split(param, ":") if len(parts) != 3 { - return fmt.Errorf("invalid format") + return errors.New("invalid format") } f.Protocol = parts[0] f.Path = parts[1] @@ -556,7 +556,7 @@ func (p *customPath) UnmarshalParam(param string) error { elems := strings.Split(param, "/") n := len(elems) if n < 2 { - return fmt.Errorf("invalid format") + return errors.New("invalid format") } *p = elems @@ -600,7 +600,7 @@ func (o *objectID) UnmarshalParam(param string) error { func convertTo(s string) (objectID, error) { var nilObjectID objectID if len(s) != 24 { - return nilObjectID, fmt.Errorf("invalid format") + return nilObjectID, errors.New("invalid format") } var oid [12]byte diff --git a/context_test.go b/context_test.go index 6921deabe6..7c414843e2 100644 --- a/context_test.go +++ b/context_test.go @@ -18,6 +18,7 @@ import ( "net/url" "os" "reflect" + "strconv" "strings" "sync" "testing" @@ -2633,7 +2634,7 @@ func TestContextRenderDataFromReader(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, body, w.Body.String()) assert.Equal(t, contentType, w.Header().Get("Content-Type")) - assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length")) + assert.Equal(t, strconv.FormatInt(contentLength, 10), w.Header().Get("Content-Length")) assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition")) } @@ -2651,7 +2652,7 @@ func TestContextRenderDataFromReaderNoHeaders(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, body, w.Body.String()) assert.Equal(t, contentType, w.Header().Get("Content-Type")) - assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length")) + assert.Equal(t, strconv.FormatInt(contentLength, 10), w.Header().Get("Content-Length")) } type TestResponseRecorder struct { diff --git a/gin_test.go b/gin_test.go index 719f63e454..5d0c47d38a 100644 --- a/gin_test.go +++ b/gin_test.go @@ -73,7 +73,7 @@ func TestLoadHTMLGlobDebugMode(t *testing.T) { ) defer ts.Close() - res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) + res, err := http.Get(ts.URL + "/test") if err != nil { t.Error(err) } @@ -131,7 +131,7 @@ func TestLoadHTMLGlobTestMode(t *testing.T) { ) defer ts.Close() - res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) + res, err := http.Get(ts.URL + "/test") if err != nil { t.Error(err) } @@ -151,7 +151,7 @@ func TestLoadHTMLGlobReleaseMode(t *testing.T) { ) defer ts.Close() - res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) + res, err := http.Get(ts.URL + "/test") if err != nil { t.Error(err) } @@ -178,7 +178,7 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) { }, } client := &http.Client{Transport: tr} - res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) + res, err := client.Get(ts.URL + "/test") if err != nil { t.Error(err) } @@ -198,7 +198,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) { ) defer ts.Close() - res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) + res, err := http.Get(ts.URL + "/raw") if err != nil { t.Error(err) } @@ -229,7 +229,7 @@ func TestLoadHTMLFilesTestMode(t *testing.T) { ) defer ts.Close() - res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) + res, err := http.Get(ts.URL + "/test") if err != nil { t.Error(err) } @@ -249,7 +249,7 @@ func TestLoadHTMLFilesDebugMode(t *testing.T) { ) defer ts.Close() - res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) + res, err := http.Get(ts.URL + "/test") if err != nil { t.Error(err) } @@ -269,7 +269,7 @@ func TestLoadHTMLFilesReleaseMode(t *testing.T) { ) defer ts.Close() - res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) + res, err := http.Get(ts.URL + "/test") if err != nil { t.Error(err) } @@ -296,7 +296,7 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) { }, } client := &http.Client{Transport: tr} - res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) + res, err := client.Get(ts.URL + "/test") if err != nil { t.Error(err) } @@ -316,7 +316,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) { ) defer ts.Close() - res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) + res, err := http.Get(ts.URL + "/raw") if err != nil { t.Error(err) } diff --git a/githubapi_test.go b/githubapi_test.go index 6d34878720..0c86af2e30 100644 --- a/githubapi_test.go +++ b/githubapi_test.go @@ -10,6 +10,7 @@ import ( "net/http" "net/http/httptest" "os" + "strconv" "strings" "testing" @@ -411,7 +412,7 @@ func exampleFromPath(path string) (string, Params) { } if start >= 0 { if c == '/' { - value := fmt.Sprint(rand.Intn(100000)) + value := strconv.Itoa(rand.Intn(100000)) params = append(params, Param{ Key: path[start:i], Value: value, @@ -425,7 +426,7 @@ func exampleFromPath(path string) (string, Params) { } } if start >= 0 { - value := fmt.Sprint(rand.Intn(100000)) + value := strconv.Itoa(rand.Intn(100000)) params = append(params, Param{ Key: path[start:], Value: value, diff --git a/recovery_test.go b/recovery_test.go index fa8ab894be..ee063cd1dc 100644 --- a/recovery_test.go +++ b/recovery_test.go @@ -5,7 +5,6 @@ package gin import ( - "fmt" "net" "net/http" "os" @@ -33,7 +32,7 @@ func TestPanicClean(t *testing.T) { }, header{ Key: "Authorization", - Value: fmt.Sprintf("Bearer %s", password), + Value: "Bearer " + password, }, header{ Key: "Content-Type", diff --git a/routes_test.go b/routes_test.go index d6233b0904..49f355a79d 100644 --- a/routes_test.go +++ b/routes_test.go @@ -560,7 +560,7 @@ func TestRouterNotFoundWithRemoveExtraSlash(t *testing.T) { w := PerformRequest(router, "GET", tr.route) assert.Equal(t, tr.code, w.Code) if w.Code != http.StatusNotFound { - assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) + assert.Equal(t, tr.location, w.Header().Get("Location")) } } } @@ -590,7 +590,7 @@ func TestRouterNotFound(t *testing.T) { w := PerformRequest(router, http.MethodGet, tr.route) assert.Equal(t, tr.code, w.Code) if w.Code != http.StatusNotFound { - assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) + assert.Equal(t, tr.location, w.Header().Get("Location")) } } From e8d34d053f7008858886b8e4f76b3e8564105870 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Fri, 15 Nov 2024 16:52:16 +0100 Subject: [PATCH 342/346] ci(lint): enable usestdlibvars linter (#4091) Signed-off-by: Matthieu MOREL --- .golangci.yml | 1 + auth_test.go | 10 +- benchmarks_test.go | 24 +-- binding/binding_msgpack_test.go | 9 +- binding/binding_test.go | 232 ++++++++++++------------- binding/multipart_form_mapping_test.go | 2 +- context_test.go | 154 ++++++++-------- debug_test.go | 5 +- deprecated_test.go | 2 +- gin_test.go | 48 ++--- logger_test.go | 84 ++++----- middleware_test.go | 16 +- recovery_test.go | 22 +-- render/render_test.go | 2 +- routes_test.go | 8 +- utils_test.go | 12 +- 16 files changed, 317 insertions(+), 314 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index c3ae727523..ccb26684fe 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,6 +19,7 @@ linters: - perfsprint - revive - testifylint + - usestdlibvars - wastedassign linters-settings: diff --git a/auth_test.go b/auth_test.go index f7175929b4..9166e3b0f1 100644 --- a/auth_test.go +++ b/auth_test.go @@ -90,7 +90,7 @@ func TestBasicAuthSucceed(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/login", nil) + req, _ := http.NewRequest(http.MethodGet, "/login", nil) req.Header.Set("Authorization", authorizationHeader("admin", "password")) router.ServeHTTP(w, req) @@ -109,7 +109,7 @@ func TestBasicAuth401(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/login", nil) + req, _ := http.NewRequest(http.MethodGet, "/login", nil) req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password"))) router.ServeHTTP(w, req) @@ -129,7 +129,7 @@ func TestBasicAuth401WithCustomRealm(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/login", nil) + req, _ := http.NewRequest(http.MethodGet, "/login", nil) req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password"))) router.ServeHTTP(w, req) @@ -147,7 +147,7 @@ func TestBasicAuthForProxySucceed(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/test", nil) + req, _ := http.NewRequest(http.MethodGet, "/test", nil) req.Header.Set("Proxy-Authorization", authorizationHeader("admin", "password")) router.ServeHTTP(w, req) @@ -166,7 +166,7 @@ func TestBasicAuthForProxy407(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/test", nil) + req, _ := http.NewRequest(http.MethodGet, "/test", nil) req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password"))) router.ServeHTTP(w, req) diff --git a/benchmarks_test.go b/benchmarks_test.go index 5b7929b854..3a8d53f3dd 100644 --- a/benchmarks_test.go +++ b/benchmarks_test.go @@ -14,21 +14,21 @@ import ( func BenchmarkOneRoute(B *testing.B) { router := New() router.GET("/ping", func(c *Context) {}) - runRequest(B, router, "GET", "/ping") + runRequest(B, router, http.MethodGet, "/ping") } func BenchmarkRecoveryMiddleware(B *testing.B) { router := New() router.Use(Recovery()) router.GET("/", func(c *Context) {}) - runRequest(B, router, "GET", "/") + runRequest(B, router, http.MethodGet, "/") } func BenchmarkLoggerMiddleware(B *testing.B) { router := New() router.Use(LoggerWithWriter(newMockWriter())) router.GET("/", func(c *Context) {}) - runRequest(B, router, "GET", "/") + runRequest(B, router, http.MethodGet, "/") } func BenchmarkManyHandlers(B *testing.B) { @@ -37,7 +37,7 @@ func BenchmarkManyHandlers(B *testing.B) { router.Use(func(c *Context) {}) router.Use(func(c *Context) {}) router.GET("/ping", func(c *Context) {}) - runRequest(B, router, "GET", "/ping") + runRequest(B, router, http.MethodGet, "/ping") } func Benchmark5Params(B *testing.B) { @@ -45,7 +45,7 @@ func Benchmark5Params(B *testing.B) { router := New() router.Use(func(c *Context) {}) router.GET("/param/:param1/:params2/:param3/:param4/:param5", func(c *Context) {}) - runRequest(B, router, "GET", "/param/path/to/parameter/john/12345") + runRequest(B, router, http.MethodGet, "/param/path/to/parameter/john/12345") } func BenchmarkOneRouteJSON(B *testing.B) { @@ -56,7 +56,7 @@ func BenchmarkOneRouteJSON(B *testing.B) { router.GET("/json", func(c *Context) { c.JSON(http.StatusOK, data) }) - runRequest(B, router, "GET", "/json") + runRequest(B, router, http.MethodGet, "/json") } func BenchmarkOneRouteHTML(B *testing.B) { @@ -68,7 +68,7 @@ func BenchmarkOneRouteHTML(B *testing.B) { router.GET("/html", func(c *Context) { c.HTML(http.StatusOK, "index", "hola") }) - runRequest(B, router, "GET", "/html") + runRequest(B, router, http.MethodGet, "/html") } func BenchmarkOneRouteSet(B *testing.B) { @@ -76,7 +76,7 @@ func BenchmarkOneRouteSet(B *testing.B) { router.GET("/ping", func(c *Context) { c.Set("key", "value") }) - runRequest(B, router, "GET", "/ping") + runRequest(B, router, http.MethodGet, "/ping") } func BenchmarkOneRouteString(B *testing.B) { @@ -84,13 +84,13 @@ func BenchmarkOneRouteString(B *testing.B) { router.GET("/text", func(c *Context) { c.String(http.StatusOK, "this is a plain text") }) - runRequest(B, router, "GET", "/text") + runRequest(B, router, http.MethodGet, "/text") } func BenchmarkManyRoutesFist(B *testing.B) { router := New() router.Any("/ping", func(c *Context) {}) - runRequest(B, router, "GET", "/ping") + runRequest(B, router, http.MethodGet, "/ping") } func BenchmarkManyRoutesLast(B *testing.B) { @@ -103,7 +103,7 @@ func Benchmark404(B *testing.B) { router := New() router.Any("/something", func(c *Context) {}) router.NoRoute(func(c *Context) {}) - runRequest(B, router, "GET", "/ping") + runRequest(B, router, http.MethodGet, "/ping") } func Benchmark404Many(B *testing.B) { @@ -118,7 +118,7 @@ func Benchmark404Many(B *testing.B) { router.GET("/user/:id/:mode", func(c *Context) {}) router.NoRoute(func(c *Context) {}) - runRequest(B, router, "GET", "/viewfake") + runRequest(B, router, http.MethodGet, "/viewfake") } type mockWriter struct { diff --git a/binding/binding_msgpack_test.go b/binding/binding_msgpack_test.go index a8116391fe..7a5db34b1a 100644 --- a/binding/binding_msgpack_test.go +++ b/binding/binding_msgpack_test.go @@ -8,6 +8,7 @@ package binding import ( "bytes" + "net/http" "testing" "github.com/stretchr/testify/assert" @@ -39,20 +40,20 @@ func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, assert.Equal(t, name, b.Name()) obj := FooStruct{} - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) req.Header.Add("Content-Type", MIMEMSGPACK) err := b.Bind(req, &obj) require.NoError(t, err) assert.Equal(t, "bar", obj.Foo) obj = FooStruct{} - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) req.Header.Add("Content-Type", MIMEMSGPACK) err = MsgPack.Bind(req, &obj) require.Error(t, err) } func TestBindingDefaultMsgPack(t *testing.T) { - assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK)) - assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2)) + assert.Equal(t, MsgPack, Default(http.MethodPost, MIMEMSGPACK)) + assert.Equal(t, MsgPack, Default(http.MethodPut, MIMEMSGPACK2)) } diff --git a/binding/binding_test.go b/binding/binding_test.go index 2036b59b91..901e974060 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -145,31 +145,31 @@ type FooStructForMapPtrType struct { } func TestBindingDefault(t *testing.T) { - assert.Equal(t, Form, Default("GET", "")) - assert.Equal(t, Form, Default("GET", MIMEJSON)) + assert.Equal(t, Form, Default(http.MethodGet, "")) + assert.Equal(t, Form, Default(http.MethodGet, MIMEJSON)) - assert.Equal(t, JSON, Default("POST", MIMEJSON)) - assert.Equal(t, JSON, Default("PUT", MIMEJSON)) + assert.Equal(t, JSON, Default(http.MethodPost, MIMEJSON)) + assert.Equal(t, JSON, Default(http.MethodPut, MIMEJSON)) - assert.Equal(t, XML, Default("POST", MIMEXML)) - assert.Equal(t, XML, Default("PUT", MIMEXML2)) + assert.Equal(t, XML, Default(http.MethodPost, MIMEXML)) + assert.Equal(t, XML, Default(http.MethodPut, MIMEXML2)) - assert.Equal(t, Form, Default("POST", MIMEPOSTForm)) - assert.Equal(t, Form, Default("PUT", MIMEPOSTForm)) + assert.Equal(t, Form, Default(http.MethodPost, MIMEPOSTForm)) + assert.Equal(t, Form, Default(http.MethodPut, MIMEPOSTForm)) - assert.Equal(t, FormMultipart, Default("POST", MIMEMultipartPOSTForm)) - assert.Equal(t, FormMultipart, Default("PUT", MIMEMultipartPOSTForm)) + assert.Equal(t, FormMultipart, Default(http.MethodPost, MIMEMultipartPOSTForm)) + assert.Equal(t, FormMultipart, Default(http.MethodPut, MIMEMultipartPOSTForm)) - assert.Equal(t, ProtoBuf, Default("POST", MIMEPROTOBUF)) - assert.Equal(t, ProtoBuf, Default("PUT", MIMEPROTOBUF)) + assert.Equal(t, ProtoBuf, Default(http.MethodPost, MIMEPROTOBUF)) + assert.Equal(t, ProtoBuf, Default(http.MethodPut, MIMEPROTOBUF)) - assert.Equal(t, YAML, Default("POST", MIMEYAML)) - assert.Equal(t, YAML, Default("PUT", MIMEYAML)) - assert.Equal(t, YAML, Default("POST", MIMEYAML2)) - assert.Equal(t, YAML, Default("PUT", MIMEYAML2)) + assert.Equal(t, YAML, Default(http.MethodPost, MIMEYAML)) + assert.Equal(t, YAML, Default(http.MethodPut, MIMEYAML)) + assert.Equal(t, YAML, Default(http.MethodPost, MIMEYAML2)) + assert.Equal(t, YAML, Default(http.MethodPut, MIMEYAML2)) - assert.Equal(t, TOML, Default("POST", MIMETOML)) - assert.Equal(t, TOML, Default("PUT", MIMETOML)) + assert.Equal(t, TOML, Default(http.MethodPost, MIMETOML)) + assert.Equal(t, TOML, Default(http.MethodPut, MIMETOML)) } func TestBindingJSONNilBody(t *testing.T) { @@ -227,137 +227,137 @@ func TestBindingJSONStringMap(t *testing.T) { } func TestBindingForm(t *testing.T) { - testFormBinding(t, "POST", + testFormBinding(t, http.MethodPost, "/", "/", "foo=bar&bar=foo", "bar2=foo") } func TestBindingForm2(t *testing.T) { - testFormBinding(t, "GET", + testFormBinding(t, http.MethodGet, "/?foo=bar&bar=foo", "/?bar2=foo", "", "") } func TestBindingFormEmbeddedStruct(t *testing.T) { - testFormBindingEmbeddedStruct(t, "POST", + testFormBindingEmbeddedStruct(t, http.MethodPost, "/", "/", "page=1&size=2&appkey=test-appkey", "bar2=foo") } func TestBindingFormEmbeddedStruct2(t *testing.T) { - testFormBindingEmbeddedStruct(t, "GET", + testFormBindingEmbeddedStruct(t, http.MethodGet, "/?page=1&size=2&appkey=test-appkey", "/?bar2=foo", "", "") } func TestBindingFormDefaultValue(t *testing.T) { - testFormBindingDefaultValue(t, "POST", + testFormBindingDefaultValue(t, http.MethodPost, "/", "/", "foo=bar", "bar2=foo") } func TestBindingFormDefaultValue2(t *testing.T) { - testFormBindingDefaultValue(t, "GET", + testFormBindingDefaultValue(t, http.MethodGet, "/?foo=bar", "/?bar2=foo", "", "") } func TestBindingFormForTime(t *testing.T) { - testFormBindingForTime(t, "POST", + testFormBindingForTime(t, http.MethodPost, "/", "/", "time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "bar2=foo") - testFormBindingForTimeNotUnixFormat(t, "POST", + testFormBindingForTimeNotUnixFormat(t, http.MethodPost, "/", "/", "time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo") - testFormBindingForTimeNotFormat(t, "POST", + testFormBindingForTimeNotFormat(t, http.MethodPost, "/", "/", "time_foo=2017-11-15", "bar2=foo") - testFormBindingForTimeFailFormat(t, "POST", + testFormBindingForTimeFailFormat(t, http.MethodPost, "/", "/", "time_foo=2017-11-15", "bar2=foo") - testFormBindingForTimeFailLocation(t, "POST", + testFormBindingForTimeFailLocation(t, http.MethodPost, "/", "/", "time_foo=2017-11-15", "bar2=foo") } func TestBindingFormForTime2(t *testing.T) { - testFormBindingForTime(t, "GET", + testFormBindingForTime(t, http.MethodGet, "/?time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "/?bar2=foo", "", "") - testFormBindingForTimeNotUnixFormat(t, "POST", + testFormBindingForTimeNotUnixFormat(t, http.MethodPost, "/", "/", "time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo") - testFormBindingForTimeNotFormat(t, "GET", + testFormBindingForTimeNotFormat(t, http.MethodGet, "/?time_foo=2017-11-15", "/?bar2=foo", "", "") - testFormBindingForTimeFailFormat(t, "GET", + testFormBindingForTimeFailFormat(t, http.MethodGet, "/?time_foo=2017-11-15", "/?bar2=foo", "", "") - testFormBindingForTimeFailLocation(t, "GET", + testFormBindingForTimeFailLocation(t, http.MethodGet, "/?time_foo=2017-11-15", "/?bar2=foo", "", "") } func TestFormBindingIgnoreField(t *testing.T) { - testFormBindingIgnoreField(t, "POST", + testFormBindingIgnoreField(t, http.MethodPost, "/", "/", "-=bar", "") } func TestBindingFormInvalidName(t *testing.T) { - testFormBindingInvalidName(t, "POST", + testFormBindingInvalidName(t, http.MethodPost, "/", "/", "test_name=bar", "bar2=foo") } func TestBindingFormInvalidName2(t *testing.T) { - testFormBindingInvalidName2(t, "POST", + testFormBindingInvalidName2(t, http.MethodPost, "/", "/", "map_foo=bar", "bar2=foo") } func TestBindingFormForType(t *testing.T) { - testFormBindingForType(t, "POST", + testFormBindingForType(t, http.MethodPost, "/", "/", "map_foo={\"bar\":123}", "map_foo=1", "Map") - testFormBindingForType(t, "POST", + testFormBindingForType(t, http.MethodPost, "/", "/", "slice_foo=1&slice_foo=2", "bar2=1&bar2=2", "Slice") - testFormBindingForType(t, "GET", + testFormBindingForType(t, http.MethodGet, "/?slice_foo=1&slice_foo=2", "/?bar2=1&bar2=2", "", "", "Slice") - testFormBindingForType(t, "POST", + testFormBindingForType(t, http.MethodPost, "/", "/", "slice_map_foo=1&slice_map_foo=2", "bar2=1&bar2=2", "SliceMap") - testFormBindingForType(t, "GET", + testFormBindingForType(t, http.MethodGet, "/?slice_map_foo=1&slice_map_foo=2", "/?bar2=1&bar2=2", "", "", "SliceMap") - testFormBindingForType(t, "POST", + testFormBindingForType(t, http.MethodPost, "/", "/", "ptr_bar=test", "bar2=test", "Ptr") - testFormBindingForType(t, "GET", + testFormBindingForType(t, http.MethodGet, "/?ptr_bar=test", "/?bar2=test", "", "", "Ptr") - testFormBindingForType(t, "POST", + testFormBindingForType(t, http.MethodPost, "/", "/", "idx=123", "id1=1", "Struct") - testFormBindingForType(t, "GET", + testFormBindingForType(t, http.MethodGet, "/?idx=123", "/?id1=1", "", "", "Struct") - testFormBindingForType(t, "POST", + testFormBindingForType(t, http.MethodPost, "/", "/", "name=thinkerou", "name1=ou", "StructPointer") - testFormBindingForType(t, "GET", + testFormBindingForType(t, http.MethodGet, "/?name=thinkerou", "/?name1=ou", "", "", "StructPointer") } @@ -374,7 +374,7 @@ func TestBindingFormStringMap(t *testing.T) { func TestBindingFormStringSliceMap(t *testing.T) { obj := make(map[string][]string) - req := requestWithBody("POST", "/", "foo=something&foo=bar&hello=world") + req := requestWithBody(http.MethodPost, "/", "foo=something&foo=bar&hello=world") req.Header.Add("Content-Type", MIMEPOSTForm) err := Form.Bind(req, &obj) require.NoError(t, err) @@ -387,38 +387,38 @@ func TestBindingFormStringSliceMap(t *testing.T) { assert.True(t, reflect.DeepEqual(obj, target)) objInvalid := make(map[string][]int) - req = requestWithBody("POST", "/", "foo=something&foo=bar&hello=world") + req = requestWithBody(http.MethodPost, "/", "foo=something&foo=bar&hello=world") req.Header.Add("Content-Type", MIMEPOSTForm) err = Form.Bind(req, &objInvalid) require.Error(t, err) } func TestBindingQuery(t *testing.T) { - testQueryBinding(t, "POST", + testQueryBinding(t, http.MethodPost, "/?foo=bar&bar=foo", "/", "foo=unused", "bar2=foo") } func TestBindingQuery2(t *testing.T) { - testQueryBinding(t, "GET", + testQueryBinding(t, http.MethodGet, "/?foo=bar&bar=foo", "/?bar2=foo", "foo=unused", "") } func TestBindingQueryFail(t *testing.T) { - testQueryBindingFail(t, "POST", + testQueryBindingFail(t, http.MethodPost, "/?map_foo=", "/", "map_foo=unused", "bar2=foo") } func TestBindingQueryFail2(t *testing.T) { - testQueryBindingFail(t, "GET", + testQueryBindingFail(t, http.MethodGet, "/?map_foo=", "/?bar2=foo", "map_foo=unused", "") } func TestBindingQueryBoolFail(t *testing.T) { - testQueryBindingBoolFail(t, "GET", + testQueryBindingBoolFail(t, http.MethodGet, "/?bool_foo=fasl", "/?bar2=foo", "bool_foo=unused", "") } @@ -427,7 +427,7 @@ func TestBindingQueryStringMap(t *testing.T) { b := Query obj := make(map[string]string) - req := requestWithBody("GET", "/?foo=bar&hello=world", "") + req := requestWithBody(http.MethodGet, "/?foo=bar&hello=world", "") err := b.Bind(req, &obj) require.NoError(t, err) assert.NotNil(t, obj) @@ -436,7 +436,7 @@ func TestBindingQueryStringMap(t *testing.T) { assert.Equal(t, "world", obj["hello"]) obj = make(map[string]string) - req = requestWithBody("GET", "/?foo=bar&foo=2&hello=world", "") // should pick last + req = requestWithBody(http.MethodGet, "/?foo=bar&foo=2&hello=world", "") // should pick last err = b.Bind(req, &obj) require.NoError(t, err) assert.NotNil(t, obj) @@ -495,28 +495,28 @@ func TestBindingYAMLFail(t *testing.T) { } func createFormPostRequest(t *testing.T) *http.Request { - req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo")) + req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo")) require.NoError(t, err) req.Header.Set("Content-Type", MIMEPOSTForm) return req } func createDefaultFormPostRequest(t *testing.T) *http.Request { - req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar")) + req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar")) require.NoError(t, err) req.Header.Set("Content-Type", MIMEPOSTForm) return req } func createFormPostRequestForMap(t *testing.T) *http.Request { - req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}")) + req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}")) require.NoError(t, err) req.Header.Set("Content-Type", MIMEPOSTForm) return req } func createFormPostRequestForMapFail(t *testing.T) *http.Request { - req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello")) + req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello")) require.NoError(t, err) req.Header.Set("Content-Type", MIMEPOSTForm) return req @@ -540,7 +540,7 @@ func createFormFilesMultipartRequest(t *testing.T) *http.Request { _, err = io.Copy(fw, f) require.NoError(t, err) - req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) + req, err2 := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body) require.NoError(t, err2) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) @@ -565,7 +565,7 @@ func createFormFilesMultipartRequestFail(t *testing.T) *http.Request { _, err = io.Copy(fw, f) require.NoError(t, err) - req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) + req, err2 := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body) require.NoError(t, err2) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) @@ -581,7 +581,7 @@ func createFormMultipartRequest(t *testing.T) *http.Request { require.NoError(t, mw.SetBoundary(boundary)) require.NoError(t, mw.WriteField("foo", "bar")) require.NoError(t, mw.WriteField("bar", "foo")) - req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) + req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body) require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) return req @@ -595,7 +595,7 @@ func createFormMultipartRequestForMap(t *testing.T) *http.Request { require.NoError(t, mw.SetBoundary(boundary)) require.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}")) - req, err := http.NewRequest("POST", "/?map_foo=getfoo", body) + req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", body) require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) return req @@ -609,7 +609,7 @@ func createFormMultipartRequestForMapFail(t *testing.T) *http.Request { require.NoError(t, mw.SetBoundary(boundary)) require.NoError(t, mw.WriteField("map_foo", "3.14")) - req, err := http.NewRequest("POST", "/?map_foo=getfoo", body) + req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", body) require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) return req @@ -731,7 +731,7 @@ func TestBindingProtoBufFail(t *testing.T) { func TestValidationFails(t *testing.T) { var obj FooStruct - req := requestWithBody("POST", "/", `{"bar": "foo"}`) + req := requestWithBody(http.MethodPost, "/", `{"bar": "foo"}`) err := JSON.Bind(req, &obj) require.Error(t, err) } @@ -742,7 +742,7 @@ func TestValidationDisabled(t *testing.T) { defer func() { Validator = backup }() var obj FooStruct - req := requestWithBody("POST", "/", `{"bar": "foo"}`) + req := requestWithBody(http.MethodPost, "/", `{"bar": "foo"}`) err := JSON.Bind(req, &obj) require.NoError(t, err) } @@ -753,7 +753,7 @@ func TestRequiredSucceeds(t *testing.T) { } var obj HogeStruct - req := requestWithBody("POST", "/", `{"hoge": 0}`) + req := requestWithBody(http.MethodPost, "/", `{"hoge": 0}`) err := JSON.Bind(req, &obj) require.NoError(t, err) } @@ -764,7 +764,7 @@ func TestRequiredFails(t *testing.T) { } var obj HogeStruct - req := requestWithBody("POST", "/", `{"boen": 0}`) + req := requestWithBody(http.MethodPost, "/", `{"boen": 0}`) err := JSON.Bind(req, &obj) require.Error(t, err) } @@ -778,12 +778,12 @@ func TestHeaderBinding(t *testing.T) { } var theader tHeader - req := requestWithBody("GET", "/", "") + req := requestWithBody(http.MethodGet, "/", "") req.Header.Add("limit", "1000") require.NoError(t, h.Bind(req, &theader)) assert.Equal(t, 1000, theader.Limit) - req = requestWithBody("GET", "/", "") + req = requestWithBody(http.MethodGet, "/", "") req.Header.Add("fail", `{fail:fail}`) type failStruct struct { @@ -843,7 +843,7 @@ func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, ba obj := QueryTest{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -859,7 +859,7 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) obj := FooBarStruct{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -879,7 +879,7 @@ func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badB obj := FooDefaultBarStruct{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -898,14 +898,14 @@ func TestFormBindingFail(t *testing.T) { assert.Equal(t, "form", b.Name()) obj := FooBarStruct{} - req, _ := http.NewRequest("POST", "/", nil) + req, _ := http.NewRequest(http.MethodPost, "/", nil) err := b.Bind(req, &obj) require.Error(t, err) } func TestFormBindingMultipartFail(t *testing.T) { obj := FooBarStruct{} - req, err := http.NewRequest("POST", "/", strings.NewReader("foo=bar")) + req, err := http.NewRequest(http.MethodPost, "/", strings.NewReader("foo=bar")) require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+";boundary=testboundary") _, err = req.MultipartReader() @@ -919,7 +919,7 @@ func TestFormPostBindingFail(t *testing.T) { assert.Equal(t, "form-urlencoded", b.Name()) obj := FooBarStruct{} - req, _ := http.NewRequest("POST", "/", nil) + req, _ := http.NewRequest(http.MethodPost, "/", nil) err := b.Bind(req, &obj) require.Error(t, err) } @@ -929,7 +929,7 @@ func TestFormMultipartBindingFail(t *testing.T) { assert.Equal(t, "multipart/form-data", b.Name()) obj := FooBarStruct{} - req, _ := http.NewRequest("POST", "/", nil) + req, _ := http.NewRequest(http.MethodPost, "/", nil) err := b.Bind(req, &obj) require.Error(t, err) } @@ -940,7 +940,7 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s obj := FooBarStructForTimeType{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -965,7 +965,7 @@ func testFormBindingForTimeNotUnixFormat(t *testing.T, method, path, badPath, bo obj := FooStructForTimeTypeNotUnixFormat{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -983,7 +983,7 @@ func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body, obj := FooStructForTimeTypeNotFormat{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -1001,7 +1001,7 @@ func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body, obj := FooStructForTimeTypeFailFormat{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -1019,7 +1019,7 @@ func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, bod obj := FooStructForTimeTypeFailLocation{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -1037,7 +1037,7 @@ func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBo obj := FooStructForIgnoreFormTag{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -1052,7 +1052,7 @@ func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBo obj := InvalidNameType{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -1071,7 +1071,7 @@ func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badB obj := InvalidNameMapType{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -1088,7 +1088,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s assert.Equal(t, "form", b.Name()) req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } switch typ { @@ -1159,7 +1159,7 @@ func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string) obj := FooBarStruct{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -1174,7 +1174,7 @@ func testQueryBindingFail(t *testing.T, method, path, badPath, body, badBody str obj := FooStructForMapType{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -1187,7 +1187,7 @@ func testQueryBindingBoolFail(t *testing.T, method, path, badPath, body, badBody obj := FooStructForBoolType{} req := requestWithBody(method, path, body) - if method == "POST" { + if method == http.MethodPost { req.Header.Add("Content-Type", MIMEPOSTForm) } err := b.Bind(req, &obj) @@ -1198,13 +1198,13 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody assert.Equal(t, name, b.Name()) obj := FooStruct{} - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) err := b.Bind(req, &obj) require.NoError(t, err) assert.Equal(t, "bar", obj.Foo) obj = FooStruct{} - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) err = JSON.Bind(req, &obj) require.Error(t, err) } @@ -1213,19 +1213,19 @@ func testBodyBindingSlice(t *testing.T, b Binding, name, path, badPath, body, ba assert.Equal(t, name, b.Name()) var obj1 []FooStruct - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) err := b.Bind(req, &obj1) require.NoError(t, err) var obj2 []FooStruct - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) err = JSON.Bind(req, &obj2) require.Error(t, err) } func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) { obj := make(map[string]string) - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) if b.Name() == "form" { req.Header.Add("Content-Type", MIMEPOSTForm) } @@ -1238,13 +1238,13 @@ func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badB if badPath != "" && badBody != "" { obj = make(map[string]string) - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) err = b.Bind(req, &obj) require.Error(t, err) } objInt := make(map[string]int) - req = requestWithBody("POST", path, body) + req = requestWithBody(http.MethodPost, path, body) err = b.Bind(req, &objInt) require.Error(t, err) } @@ -1253,7 +1253,7 @@ func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body assert.Equal(t, name, b.Name()) obj := FooStructUseNumber{} - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) EnableDecoderUseNumber = true err := b.Bind(req, &obj) require.NoError(t, err) @@ -1263,7 +1263,7 @@ func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body assert.Equal(t, int64(123), v) obj = FooStructUseNumber{} - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) err = JSON.Bind(req, &obj) require.Error(t, err) } @@ -1272,7 +1272,7 @@ func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, bod assert.Equal(t, name, b.Name()) obj := FooStructUseNumber{} - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) EnableDecoderUseNumber = false err := b.Bind(req, &obj) require.NoError(t, err) @@ -1281,7 +1281,7 @@ func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, bod assert.InDelta(t, float64(123), obj.Foo, 0.01) obj = FooStructUseNumber{} - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) err = JSON.Bind(req, &obj) require.Error(t, err) } @@ -1293,13 +1293,13 @@ func testBodyBindingDisallowUnknownFields(t *testing.T, b Binding, path, badPath }() obj := FooStructDisallowUnknownFields{} - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) err := b.Bind(req, &obj) require.NoError(t, err) assert.Equal(t, "bar", obj.Foo) obj = FooStructDisallowUnknownFields{} - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) err = JSON.Bind(req, &obj) require.Error(t, err) assert.Contains(t, err.Error(), "what") @@ -1309,13 +1309,13 @@ func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, bad assert.Equal(t, name, b.Name()) obj := FooStruct{} - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) err := b.Bind(req, &obj) require.Error(t, err) assert.Equal(t, "", obj.Foo) obj = FooStruct{} - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) err = JSON.Bind(req, &obj) require.Error(t, err) } @@ -1324,14 +1324,14 @@ func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, ba assert.Equal(t, name, b.Name()) obj := protoexample.Test{} - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) req.Header.Add("Content-Type", MIMEPROTOBUF) err := b.Bind(req, &obj) require.NoError(t, err) assert.Equal(t, "yes", *obj.Label) obj = protoexample.Test{} - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) req.Header.Add("Content-Type", MIMEPROTOBUF) err = ProtoBuf.Bind(req, &obj) require.Error(t, err) @@ -1358,28 +1358,28 @@ func TestPlainBinding(t *testing.T) { assert.Equal(t, "plain", p.Name()) var s string - req := requestWithBody("POST", "/", "test string") + req := requestWithBody(http.MethodPost, "/", "test string") require.NoError(t, p.Bind(req, &s)) assert.Equal(t, "test string", s) var bs []byte - req = requestWithBody("POST", "/", "test []byte") + req = requestWithBody(http.MethodPost, "/", "test []byte") require.NoError(t, p.Bind(req, &bs)) assert.Equal(t, bs, []byte("test []byte")) var i int - req = requestWithBody("POST", "/", "test fail") + req = requestWithBody(http.MethodPost, "/", "test fail") require.Error(t, p.Bind(req, &i)) - req = requestWithBody("POST", "/", "") + req = requestWithBody(http.MethodPost, "/", "") req.Body = &failRead{} require.Error(t, p.Bind(req, &s)) - req = requestWithBody("POST", "/", "") + req = requestWithBody(http.MethodPost, "/", "") require.NoError(t, p.Bind(req, nil)) var ptr *string - req = requestWithBody("POST", "/", "") + req = requestWithBody(http.MethodPost, "/", "") require.NoError(t, p.Bind(req, ptr)) } @@ -1387,7 +1387,7 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body assert.Equal(t, name, b.Name()) obj := protoexample.Test{} - req := requestWithBody("POST", path, body) + req := requestWithBody(http.MethodPost, path, body) req.Body = io.NopCloser(&hook{}) req.Header.Add("Content-Type", MIMEPROTOBUF) @@ -1402,7 +1402,7 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body assert.Equal(t, "obj is not ProtoMessage", err.Error()) obj = protoexample.Test{} - req = requestWithBody("POST", badPath, badBody) + req = requestWithBody(http.MethodPost, badPath, badBody) req.Header.Add("Content-Type", MIMEPROTOBUF) err = ProtoBuf.Bind(req, &obj) require.Error(t, err) diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go index 9782b81df8..c93f214105 100644 --- a/binding/multipart_form_mapping_test.go +++ b/binding/multipart_form_mapping_test.go @@ -116,7 +116,7 @@ func createRequestMultipartFiles(t *testing.T, files ...testFile) *http.Request err := mw.Close() require.NoError(t, err) - req, err := http.NewRequest("POST", "/", &body) + req, err := http.NewRequest(http.MethodPost, "/", &body) require.NoError(t, err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+mw.Boundary()) diff --git a/context_test.go b/context_test.go index 7c414843e2..91d5e898ff 100644 --- a/context_test.go +++ b/context_test.go @@ -60,7 +60,7 @@ func createMultipartRequest() *http.Request { must(mw.WriteField("time_location", "31/12/2016 14:55")) must(mw.WriteField("names[a]", "thinkerou")) must(mw.WriteField("names[b]", "tianou")) - req, err := http.NewRequest("POST", "/", body) + req, err := http.NewRequest(http.MethodPost, "/", body) must(err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) return req @@ -81,7 +81,7 @@ func TestContextFormFile(t *testing.T) { require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") require.NoError(t, err) @@ -95,7 +95,7 @@ func TestContextFormFileFailed(t *testing.T) { mw := multipart.NewWriter(buf) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) c.engine.MaxMultipartMemory = 8 << 20 f, err := c.FormFile("file") @@ -113,7 +113,7 @@ func TestContextMultipartForm(t *testing.T) { require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.MultipartForm() require.NoError(t, err) @@ -128,7 +128,7 @@ func TestSaveUploadedOpenFailed(t *testing.T) { mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f := &multipart.FileHeader{ @@ -146,7 +146,7 @@ func TestSaveUploadedCreateFailed(t *testing.T) { require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") require.NoError(t, err) @@ -481,7 +481,7 @@ func TestContextGetStringMapStringSlice(t *testing.T) { func TestContextCopy(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.index = 2 - c.Request, _ = http.NewRequest("POST", "/hola", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/hola", nil) c.handlers = HandlersChain{func(c *Context) {}} c.Params = Params{Param{Key: "foo", Value: "bar"}} c.Set("foo", "bar") @@ -538,7 +538,7 @@ func TestContextHandler(t *testing.T) { func TestContextQuery(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&id=", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "http://example.com/?foo=bar&page=10&id=", nil) value, ok := c.GetQuery("foo") assert.True(t, ok) @@ -631,7 +631,7 @@ func TestContextDefaultQueryOnEmptyRequest(t *testing.T) { func TestContextQueryAndPostForm(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second") - c.Request, _ = http.NewRequest("POST", + c.Request, _ = http.NewRequest(http.MethodPost, "/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body) c.Request.Header.Add("Content-Type", MIMEPOSTForm) @@ -651,7 +651,7 @@ func TestContextQueryAndPostForm(t *testing.T) { assert.Empty(t, value) assert.Empty(t, c.PostForm("both")) assert.Empty(t, c.DefaultPostForm("both", "nothing")) - assert.Equal(t, "GET", c.Query("both"), "GET") + assert.Equal(t, http.MethodGet, c.Query("both"), http.MethodGet) value, ok = c.GetQuery("id") assert.True(t, ok) @@ -699,7 +699,7 @@ func TestContextQueryAndPostForm(t *testing.T) { values = c.QueryArray("both") assert.Len(t, values, 1) - assert.Equal(t, "GET", values[0]) + assert.Equal(t, http.MethodGet, values[0]) dicts, ok := c.GetQueryMap("ids") assert.True(t, ok) @@ -834,7 +834,7 @@ func TestContextSetCookiePathEmpty(t *testing.T) { func TestContextGetCookie(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("GET", "/get", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "/get", nil) c.Request.Header.Set("Cookie", "user=gin") cookie, _ := c.Cookie("user") assert.Equal(t, "gin", cookie) @@ -886,7 +886,7 @@ func TestContextRenderJSON(t *testing.T) { func TestContextRenderJSONP(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("GET", "http://example.com/?callback=x", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "http://example.com/?callback=x", nil) c.JSONP(http.StatusCreated, H{"foo": "bar"}) @@ -900,7 +900,7 @@ func TestContextRenderJSONP(t *testing.T) { func TestContextRenderJSONPWithoutCallback(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("GET", "http://example.com", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "http://example.com", nil) c.JSONP(http.StatusCreated, H{"foo": "bar"}) @@ -1043,7 +1043,7 @@ func TestContextRenderHTML2(t *testing.T) { c, router := CreateTestContext(w) // print debug warning log when Engine.trees > 0 - router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}}) + router.addRoute(http.MethodGet, "/", HandlersChain{func(_ *Context) {}}) assert.Len(t, router.trees, 1) templ := template.Must(template.New("t").Parse(`Hello {{.name}}`)) @@ -1199,7 +1199,7 @@ func TestContextRenderFile(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("GET", "/", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "/", nil) c.File("./gin.go") assert.Equal(t, http.StatusOK, w.Code) @@ -1213,7 +1213,7 @@ func TestContextRenderFileFromFS(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("GET", "/some/path", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "/some/path", nil) c.FileFromFS("./gin.go", Dir(".", false)) assert.Equal(t, http.StatusOK, w.Code) @@ -1229,7 +1229,7 @@ func TestContextRenderAttachment(t *testing.T) { c, _ := CreateTestContext(w) newFilename := "new_filename.go" - c.Request, _ = http.NewRequest("GET", "/", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "/", nil) c.FileAttachment("./gin.go", newFilename) assert.Equal(t, 200, w.Code) @@ -1243,7 +1243,7 @@ func TestContextRenderAndEscapeAttachment(t *testing.T) { maliciousFilename := "tampering_field.sh\"; \\\"; dummy=.go" actualEscapedResponseFilename := "tampering_field.sh\\\"; \\\\\\\"; dummy=.go" - c.Request, _ = http.NewRequest("GET", "/", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "/", nil) c.FileAttachment("./gin.go", maliciousFilename) assert.Equal(t, 200, w.Code) @@ -1256,7 +1256,7 @@ func TestContextRenderUTF8Attachment(t *testing.T) { c, _ := CreateTestContext(w) newFilename := "new🧡_filename.go" - c.Request, _ = http.NewRequest("GET", "/", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "/", nil) c.FileAttachment("./gin.go", newFilename) assert.Equal(t, 200, w.Code) @@ -1335,7 +1335,7 @@ func TestContextRenderRedirectWithRelativePath(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "http://example.com", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil) assert.Panics(t, func() { c.Redirect(299, "/new_path") }) assert.Panics(t, func() { c.Redirect(309, "/new_path") }) @@ -1349,7 +1349,7 @@ func TestContextRenderRedirectWithAbsolutePath(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "http://example.com", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil) c.Redirect(http.StatusFound, "http://google.com") c.Writer.WriteHeaderNow() @@ -1361,7 +1361,7 @@ func TestContextRenderRedirectWith201(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "http://example.com", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil) c.Redirect(http.StatusCreated, "/resource") c.Writer.WriteHeaderNow() @@ -1371,7 +1371,7 @@ func TestContextRenderRedirectWith201(t *testing.T) { func TestContextRenderRedirectAll(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "http://example.com", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil) assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") }) assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") }) assert.Panics(t, func() { c.Redirect(299, "/resource") }) @@ -1383,7 +1383,7 @@ func TestContextRenderRedirectAll(t *testing.T) { func TestContextNegotiationWithJSON(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "", nil) c.Negotiate(http.StatusOK, Negotiate{ Offered: []string{MIMEJSON, MIMEXML, MIMEYAML, MIMEYAML2}, @@ -1398,7 +1398,7 @@ func TestContextNegotiationWithJSON(t *testing.T) { func TestContextNegotiationWithXML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "", nil) c.Negotiate(http.StatusOK, Negotiate{ Offered: []string{MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2}, @@ -1413,7 +1413,7 @@ func TestContextNegotiationWithXML(t *testing.T) { func TestContextNegotiationWithYAML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "", nil) c.Negotiate(http.StatusOK, Negotiate{ Offered: []string{MIMEYAML, MIMEXML, MIMEJSON, MIMETOML, MIMEYAML2}, @@ -1428,7 +1428,7 @@ func TestContextNegotiationWithYAML(t *testing.T) { func TestContextNegotiationWithTOML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "", nil) c.Negotiate(http.StatusOK, Negotiate{ Offered: []string{MIMETOML, MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2}, @@ -1443,7 +1443,7 @@ func TestContextNegotiationWithTOML(t *testing.T) { func TestContextNegotiationWithHTML(t *testing.T) { w := httptest.NewRecorder() c, router := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "", nil) templ := template.Must(template.New("t").Parse(`Hello {{.name}}`)) router.SetHTMLTemplate(templ) @@ -1461,7 +1461,7 @@ func TestContextNegotiationWithHTML(t *testing.T) { func TestContextNegotiationNotSupport(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "", nil) c.Negotiate(http.StatusOK, Negotiate{ Offered: []string{MIMEPOSTForm}, @@ -1474,7 +1474,7 @@ func TestContextNegotiationNotSupport(t *testing.T) { func TestContextNegotiationFormat(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "", nil) assert.Panics(t, func() { c.NegotiateFormat() }) assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML)) @@ -1483,7 +1483,7 @@ func TestContextNegotiationFormat(t *testing.T) { func TestContextNegotiationFormatWithAccept(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8") assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEJSON, MIMEXML)) @@ -1493,7 +1493,7 @@ func TestContextNegotiationFormatWithAccept(t *testing.T) { func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.Header.Add("Accept", "*/*") assert.Equal(t, "*/*", c.NegotiateFormat("*/*")) @@ -1504,7 +1504,7 @@ func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) { assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML)) c, _ = CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.Header.Add("Accept", "text/*") assert.Equal(t, "*/*", c.NegotiateFormat("*/*")) @@ -1517,7 +1517,7 @@ func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) { func TestContextNegotiationFormatCustom(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8") c.Accepted = nil @@ -1530,7 +1530,7 @@ func TestContextNegotiationFormatCustom(t *testing.T) { func TestContextNegotiationFormat2(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.Header.Add("Accept", "image/tiff-fx") assert.Equal(t, "", c.NegotiateFormat("image/tiff")) @@ -1658,7 +1658,7 @@ func TestContextAbortWithError(t *testing.T) { func TestContextClientIP(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.engine.trustedCIDRs, _ = c.engine.prepareTrustedCIDRs() resetContextForClientIPTests(c) @@ -1801,7 +1801,7 @@ func resetContextForClientIPTests(c *Context) { func TestContextContentType(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.Header.Set("Content-Type", "application/json; charset=utf-8") assert.Equal(t, "application/json", c.ContentType()) @@ -1809,7 +1809,7 @@ func TestContextContentType(t *testing.T) { func TestContextAutoBindJSON(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request.Header.Add("Content-Type", MIMEJSON) var obj struct { @@ -1826,7 +1826,7 @@ func TestContextBindWithJSON(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type var obj struct { @@ -1843,7 +1843,7 @@ func TestContextBindWithXML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(` + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(` FOO BAR @@ -1864,7 +1864,7 @@ func TestContextBindPlain(t *testing.T) { // string w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`)) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test string`)) c.Request.Header.Add("Content-Type", MIMEPlain) var s string @@ -1874,7 +1874,7 @@ func TestContextBindPlain(t *testing.T) { assert.Equal(t, 0, w.Body.Len()) // []byte - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`)) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test []byte`)) c.Request.Header.Add("Content-Type", MIMEPlain) var bs []byte @@ -1888,7 +1888,7 @@ func TestContextBindHeader(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.Header.Add("rate", "8000") c.Request.Header.Add("domain", "music") c.Request.Header.Add("limit", "1000") @@ -1910,7 +1910,7 @@ func TestContextBindWithQuery(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) + c.Request, _ = http.NewRequest(http.MethodPost, "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) var obj struct { Foo string `form:"foo"` @@ -1926,7 +1926,7 @@ func TestContextBindWithYAML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo")) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo: bar\nbar: foo")) c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type var obj struct { @@ -1943,7 +1943,7 @@ func TestContextBindWithTOML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo = 'bar'\nbar = 'foo'")) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo = 'bar'\nbar = 'foo'")) c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type var obj struct { @@ -1960,7 +1960,7 @@ func TestContextBadAutoBind(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request.Header.Add("Content-Type", MIMEJSON) var obj struct { Foo string `json:"foo"` @@ -1979,7 +1979,7 @@ func TestContextBadAutoBind(t *testing.T) { func TestContextAutoShouldBindJSON(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request.Header.Add("Content-Type", MIMEJSON) var obj struct { @@ -1996,7 +1996,7 @@ func TestContextShouldBindWithJSON(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type var obj struct { @@ -2013,7 +2013,7 @@ func TestContextShouldBindWithXML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(` + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(` FOO BAR @@ -2034,7 +2034,7 @@ func TestContextShouldBindPlain(t *testing.T) { // string w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`)) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test string`)) c.Request.Header.Add("Content-Type", MIMEPlain) var s string @@ -2044,7 +2044,7 @@ func TestContextShouldBindPlain(t *testing.T) { assert.Equal(t, 0, w.Body.Len()) // []byte - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`)) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test []byte`)) c.Request.Header.Add("Content-Type", MIMEPlain) var bs []byte @@ -2058,7 +2058,7 @@ func TestContextShouldBindHeader(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.Header.Add("rate", "8000") c.Request.Header.Add("domain", "music") c.Request.Header.Add("limit", "1000") @@ -2080,7 +2080,7 @@ func TestContextShouldBindWithQuery(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", bytes.NewBufferString("foo=unused")) + c.Request, _ = http.NewRequest(http.MethodPost, "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", bytes.NewBufferString("foo=unused")) var obj struct { Foo string `form:"foo"` @@ -2100,7 +2100,7 @@ func TestContextShouldBindWithYAML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo")) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo: bar\nbar: foo")) c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type var obj struct { @@ -2117,7 +2117,7 @@ func TestContextShouldBindWithTOML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'")) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'")) c.Request.Header.Add("Content-Type", MIMETOML) // set fake content-type var obj struct { @@ -2134,7 +2134,7 @@ func TestContextBadAutoShouldBind(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request.Header.Add("Content-Type", MIMEJSON) var obj struct { Foo string `json:"foo"` @@ -2198,7 +2198,7 @@ func TestContextShouldBindBodyWith(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) c.Request, _ = http.NewRequest( - "POST", "http://example.com", bytes.NewBufferString(tt.bodyA), + http.MethodPost, "http://example.com", bytes.NewBufferString(tt.bodyA), ) // When it binds to typeA and typeB, it finds the body is // not typeB but typeA. @@ -2216,7 +2216,7 @@ func TestContextShouldBindBodyWith(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) c.Request, _ = http.NewRequest( - "POST", "http://example.com", bytes.NewBufferString(tt.bodyB), + http.MethodPost, "http://example.com", bytes.NewBufferString(tt.bodyB), ) objA := typeA{} require.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA)) @@ -2263,7 +2263,7 @@ func TestContextShouldBindBodyWithJSON(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body)) type typeJSON struct { Foo string `json:"foo" binding:"required"` @@ -2327,7 +2327,7 @@ func TestContextShouldBindBodyWithXML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body)) type typeXML struct { Foo string `xml:"foo" binding:"required"` @@ -2391,7 +2391,7 @@ func TestContextShouldBindBodyWithYAML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body)) type typeYAML struct { Foo string `yaml:"foo" binding:"required"` @@ -2456,7 +2456,7 @@ func TestContextShouldBindBodyWithTOML(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body)) type typeTOML struct { Foo string `toml:"foo" binding:"required"` @@ -2525,7 +2525,7 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body)) type typeJSON struct { Foo string `json:"foo" binding:"required"` @@ -2562,7 +2562,7 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) { func TestContextGolangContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) require.NoError(t, c.Err()) assert.Nil(t, c.Done()) ti, ok := c.Deadline() @@ -2580,7 +2580,7 @@ func TestContextGolangContext(t *testing.T) { func TestWebsocketsRequired(t *testing.T) { // Example request from spec: https://tools.ietf.org/html/rfc6455#section-1.2 c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("GET", "/chat", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil) c.Request.Header.Set("Host", "server.example.com") c.Request.Header.Set("Upgrade", "websocket") c.Request.Header.Set("Connection", "Upgrade") @@ -2593,7 +2593,7 @@ func TestWebsocketsRequired(t *testing.T) { // Normal request, no websocket required. c, _ = CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("GET", "/chat", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil) c.Request.Header.Set("Host", "server.example.com") assert.False(t, c.IsWebsocket()) @@ -2601,7 +2601,7 @@ func TestWebsocketsRequired(t *testing.T) { func TestGetRequestHeaderValue(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("GET", "/chat", nil) + c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil) c.Request.Header.Set("Gin-Version", "1.0.0") assert.Equal(t, "1.0.0", c.GetHeader("Gin-Version")) @@ -2611,7 +2611,7 @@ func TestGetRequestHeaderValue(t *testing.T) { func TestContextGetRawData(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) body := bytes.NewBufferString("Fetch binary post data") - c.Request, _ = http.NewRequest("POST", "/", body) + c.Request, _ = http.NewRequest(http.MethodPost, "/", body) c.Request.Header.Add("Content-Type", MIMEPOSTForm) data, err := c.GetRawData() @@ -2740,8 +2740,8 @@ func TestRaceParamsContextCopy(t *testing.T) { }(c.Copy(), c.Param("name")) }) } - PerformRequest(router, "GET", "/name1/api") - PerformRequest(router, "GET", "/name2/api") + PerformRequest(router, http.MethodGet, "/name1/api") + PerformRequest(router, http.MethodGet, "/name2/api") wg.Wait() } @@ -2760,7 +2760,7 @@ func TestContextWithKeysMutex(t *testing.T) { func TestRemoteIPFail(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request.RemoteAddr = "[:::]:80" ip := net.ParseIP(c.RemoteIP()) trust := c.engine.isTrustedProxy(ip) @@ -2862,7 +2862,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) // enable ContextWithFallback feature flag c.engine.ContextWithFallback = true - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request = c.Request.WithContext(context.WithValue(context.TODO(), key, "value")) return c, key }, @@ -2874,7 +2874,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) // enable ContextWithFallback feature flag c.engine.ContextWithFallback = true - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value")) return c, contextKey("key") }, @@ -2897,7 +2897,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) // enable ContextWithFallback feature flag c.engine.ContextWithFallback = true - c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request, _ = http.NewRequest(http.MethodPost, "/", nil) return c, "key" }, value: nil, @@ -3029,7 +3029,7 @@ func TestInterceptedHeader(t *testing.T) { c.Header("X-Test-2", "present") c.String(http.StatusOK, "hello world") }) - c.Request = httptest.NewRequest("GET", "/", nil) + c.Request = httptest.NewRequest(http.MethodGet, "/", nil) r.HandleContext(c) // Result() has headers frozen when WriteHeaderNow() has been called // Compared to this time, this is when the response headers will be flushed diff --git a/debug_test.go b/debug_test.go index edf4bb12ec..0efbfd7850 100644 --- a/debug_test.go +++ b/debug_test.go @@ -10,6 +10,7 @@ import ( "html/template" "io" "log" + "net/http" "os" "runtime" "strings" @@ -60,7 +61,7 @@ func TestDebugPrintError(t *testing.T) { func TestDebugPrintRoutes(t *testing.T) { re := captureOutput(t, func() { SetMode(DebugMode) - debugPrintRoute("GET", "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest}) + debugPrintRoute(http.MethodGet, "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest}) SetMode(TestMode) }) assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re) @@ -72,7 +73,7 @@ func TestDebugPrintRouteFunc(t *testing.T) { } re := captureOutput(t, func() { SetMode(DebugMode) - debugPrintRoute("GET", "/path/to/route/:param1/:param2", HandlersChain{func(c *Context) {}, handlerNameTest}) + debugPrintRoute(http.MethodGet, "/path/to/route/:param1/:param2", HandlersChain{func(c *Context) {}, handlerNameTest}) SetMode(TestMode) }) assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param1/:param2 --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re) diff --git a/deprecated_test.go b/deprecated_test.go index 0240b2ec75..6c8f2a7f57 100644 --- a/deprecated_test.go +++ b/deprecated_test.go @@ -18,7 +18,7 @@ func TestBindWith(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) + c.Request, _ = http.NewRequest(http.MethodPost, "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) var obj struct { Foo string `form:"foo"` diff --git a/gin_test.go b/gin_test.go index 5d0c47d38a..732da18bc0 100644 --- a/gin_test.go +++ b/gin_test.go @@ -327,31 +327,31 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) { func TestAddRoute(t *testing.T) { router := New() - router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}}) + router.addRoute(http.MethodGet, "/", HandlersChain{func(_ *Context) {}}) assert.Len(t, router.trees, 1) - assert.NotNil(t, router.trees.get("GET")) - assert.Nil(t, router.trees.get("POST")) + assert.NotNil(t, router.trees.get(http.MethodGet)) + assert.Nil(t, router.trees.get(http.MethodPost)) - router.addRoute("POST", "/", HandlersChain{func(_ *Context) {}}) + router.addRoute(http.MethodPost, "/", HandlersChain{func(_ *Context) {}}) assert.Len(t, router.trees, 2) - assert.NotNil(t, router.trees.get("GET")) - assert.NotNil(t, router.trees.get("POST")) + assert.NotNil(t, router.trees.get(http.MethodGet)) + assert.NotNil(t, router.trees.get(http.MethodPost)) - router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}}) + router.addRoute(http.MethodPost, "/post", HandlersChain{func(_ *Context) {}}) assert.Len(t, router.trees, 2) } func TestAddRouteFails(t *testing.T) { router := New() assert.Panics(t, func() { router.addRoute("", "/", HandlersChain{func(_ *Context) {}}) }) - assert.Panics(t, func() { router.addRoute("GET", "a", HandlersChain{func(_ *Context) {}}) }) - assert.Panics(t, func() { router.addRoute("GET", "/", HandlersChain{}) }) + assert.Panics(t, func() { router.addRoute(http.MethodGet, "a", HandlersChain{func(_ *Context) {}}) }) + assert.Panics(t, func() { router.addRoute(http.MethodGet, "/", HandlersChain{}) }) - router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}}) + router.addRoute(http.MethodPost, "/post", HandlersChain{func(_ *Context) {}}) assert.Panics(t, func() { - router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}}) + router.addRoute(http.MethodPost, "/post", HandlersChain{func(_ *Context) {}}) }) } @@ -493,27 +493,27 @@ func TestListOfRoutes(t *testing.T) { assert.Len(t, list, 7) assertRoutePresent(t, list, RouteInfo{ - Method: "GET", + Method: http.MethodGet, Path: "/favicon.ico", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", }) assertRoutePresent(t, list, RouteInfo{ - Method: "GET", + Method: http.MethodGet, Path: "/", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", }) assertRoutePresent(t, list, RouteInfo{ - Method: "GET", + Method: http.MethodGet, Path: "/users/", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$", }) assertRoutePresent(t, list, RouteInfo{ - Method: "GET", + Method: http.MethodGet, Path: "/users/:id", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", }) assertRoutePresent(t, list, RouteInfo{ - Method: "POST", + Method: http.MethodPost, Path: "/users/:id", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$", }) @@ -531,7 +531,7 @@ func TestEngineHandleContext(t *testing.T) { } assert.NotPanics(t, func() { - w := PerformRequest(r, "GET", "/") + w := PerformRequest(r, http.MethodGet, "/") assert.Equal(t, 301, w.Code) }) } @@ -564,7 +564,7 @@ func TestEngineHandleContextManyReEntries(t *testing.T) { }) assert.NotPanics(t, func() { - w := PerformRequest(r, "GET", "/"+strconv.Itoa(expectValue-1)) // include 0 value + w := PerformRequest(r, http.MethodGet, "/"+strconv.Itoa(expectValue-1)) // include 0 value assert.Equal(t, 200, w.Code) assert.Equal(t, expectValue, w.Body.Len()) }) @@ -712,8 +712,8 @@ func TestNewOptionFunc(t *testing.T) { r := New(fc) routes := r.Routes() - assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"}) - assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"}) + assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest1"}) + assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest2"}) } func TestWithOptionFunc(t *testing.T) { @@ -729,8 +729,8 @@ func TestWithOptionFunc(t *testing.T) { }) routes := r.Routes() - assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"}) - assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"}) + assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest1"}) + assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest2"}) } type Birthday string @@ -749,7 +749,7 @@ func TestCustomUnmarshalStruct(t *testing.T) { _ = ctx.BindQuery(&request) ctx.JSON(200, request.Birthday) }) - req := httptest.NewRequest("GET", "/test?birthday=2000-01-01", nil) + req := httptest.NewRequest(http.MethodGet, "/test?birthday=2000-01-01", nil) w := httptest.NewRecorder() route.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) @@ -761,7 +761,7 @@ func TestMethodNotAllowedNoRoute(t *testing.T) { g := New() g.HandleMethodNotAllowed = true - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(http.MethodGet, "/", nil) resp := httptest.NewRecorder() assert.NotPanics(t, func() { g.ServeHTTP(resp, req) }) assert.Equal(t, http.StatusNotFound, resp.Code) diff --git a/logger_test.go b/logger_test.go index b05df740a3..de00c4991c 100644 --- a/logger_test.go +++ b/logger_test.go @@ -31,9 +31,9 @@ func TestLogger(t *testing.T) { router.HEAD("/example", func(c *Context) {}) router.OPTIONS("/example", func(c *Context) {}) - PerformRequest(router, "GET", "/example?a=100") + PerformRequest(router, http.MethodGet, "/example?a=100") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "GET") + assert.Contains(t, buffer.String(), http.MethodGet) assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "a=100") @@ -41,21 +41,21 @@ func TestLogger(t *testing.T) { // like integration tests because they test the whole logging process rather // than individual functions. Im not sure where these should go. buffer.Reset() - PerformRequest(router, "POST", "/example") + PerformRequest(router, http.MethodPost, "/example") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "POST") + assert.Contains(t, buffer.String(), http.MethodPost) assert.Contains(t, buffer.String(), "/example") buffer.Reset() - PerformRequest(router, "PUT", "/example") + PerformRequest(router, http.MethodPut, "/example") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "PUT") + assert.Contains(t, buffer.String(), http.MethodPut) assert.Contains(t, buffer.String(), "/example") buffer.Reset() - PerformRequest(router, "DELETE", "/example") + PerformRequest(router, http.MethodDelete, "/example") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "DELETE") + assert.Contains(t, buffer.String(), http.MethodDelete) assert.Contains(t, buffer.String(), "/example") buffer.Reset() @@ -77,9 +77,9 @@ func TestLogger(t *testing.T) { assert.Contains(t, buffer.String(), "/example") buffer.Reset() - PerformRequest(router, "GET", "/notfound") + PerformRequest(router, http.MethodGet, "/notfound") assert.Contains(t, buffer.String(), "404") - assert.Contains(t, buffer.String(), "GET") + assert.Contains(t, buffer.String(), http.MethodGet) assert.Contains(t, buffer.String(), "/notfound") } @@ -95,9 +95,9 @@ func TestLoggerWithConfig(t *testing.T) { router.HEAD("/example", func(c *Context) {}) router.OPTIONS("/example", func(c *Context) {}) - PerformRequest(router, "GET", "/example?a=100") + PerformRequest(router, http.MethodGet, "/example?a=100") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "GET") + assert.Contains(t, buffer.String(), http.MethodGet) assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "a=100") @@ -105,21 +105,21 @@ func TestLoggerWithConfig(t *testing.T) { // like integration tests because they test the whole logging process rather // than individual functions. Im not sure where these should go. buffer.Reset() - PerformRequest(router, "POST", "/example") + PerformRequest(router, http.MethodPost, "/example") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "POST") + assert.Contains(t, buffer.String(), http.MethodPost) assert.Contains(t, buffer.String(), "/example") buffer.Reset() - PerformRequest(router, "PUT", "/example") + PerformRequest(router, http.MethodPut, "/example") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "PUT") + assert.Contains(t, buffer.String(), http.MethodPut) assert.Contains(t, buffer.String(), "/example") buffer.Reset() - PerformRequest(router, "DELETE", "/example") + PerformRequest(router, http.MethodDelete, "/example") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "DELETE") + assert.Contains(t, buffer.String(), http.MethodDelete) assert.Contains(t, buffer.String(), "/example") buffer.Reset() @@ -141,9 +141,9 @@ func TestLoggerWithConfig(t *testing.T) { assert.Contains(t, buffer.String(), "/example") buffer.Reset() - PerformRequest(router, "GET", "/notfound") + PerformRequest(router, http.MethodGet, "/notfound") assert.Contains(t, buffer.String(), "404") - assert.Contains(t, buffer.String(), "GET") + assert.Contains(t, buffer.String(), http.MethodGet) assert.Contains(t, buffer.String(), "/notfound") } @@ -169,12 +169,12 @@ func TestLoggerWithFormatter(t *testing.T) { ) })) router.GET("/example", func(c *Context) {}) - PerformRequest(router, "GET", "/example?a=100") + PerformRequest(router, http.MethodGet, "/example?a=100") // output test assert.Contains(t, buffer.String(), "[FORMATTER TEST]") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "GET") + assert.Contains(t, buffer.String(), http.MethodGet) assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "a=100") } @@ -210,12 +210,12 @@ func TestLoggerWithConfigFormatting(t *testing.T) { gotKeys = c.Keys time.Sleep(time.Millisecond) }) - PerformRequest(router, "GET", "/example?a=100") + PerformRequest(router, http.MethodGet, "/example?a=100") // output test assert.Contains(t, buffer.String(), "[FORMATTER TEST]") assert.Contains(t, buffer.String(), "200") - assert.Contains(t, buffer.String(), "GET") + assert.Contains(t, buffer.String(), http.MethodGet) assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "a=100") @@ -225,7 +225,7 @@ func TestLoggerWithConfigFormatting(t *testing.T) { assert.Equal(t, 200, gotParam.StatusCode) assert.NotEmpty(t, gotParam.Latency) assert.Equal(t, "20.20.20.20", gotParam.ClientIP) - assert.Equal(t, "GET", gotParam.Method) + assert.Equal(t, http.MethodGet, gotParam.Method) assert.Equal(t, "/example?a=100", gotParam.Path) assert.Empty(t, gotParam.ErrorMessage) assert.Equal(t, gotKeys, gotParam.Keys) @@ -239,7 +239,7 @@ func TestDefaultLogFormatter(t *testing.T) { StatusCode: 200, Latency: time.Second * 5, ClientIP: "20.20.20.20", - Method: "GET", + Method: http.MethodGet, Path: "/", ErrorMessage: "", isTerm: false, @@ -250,7 +250,7 @@ func TestDefaultLogFormatter(t *testing.T) { StatusCode: 200, Latency: time.Second * 5, ClientIP: "20.20.20.20", - Method: "GET", + Method: http.MethodGet, Path: "/", ErrorMessage: "", isTerm: true, @@ -260,7 +260,7 @@ func TestDefaultLogFormatter(t *testing.T) { StatusCode: 200, Latency: time.Millisecond * 9876543210, ClientIP: "20.20.20.20", - Method: "GET", + Method: http.MethodGet, Path: "/", ErrorMessage: "", isTerm: true, @@ -271,7 +271,7 @@ func TestDefaultLogFormatter(t *testing.T) { StatusCode: 200, Latency: time.Millisecond * 9876543210, ClientIP: "20.20.20.20", - Method: "GET", + Method: http.MethodGet, Path: "/", ErrorMessage: "", isTerm: false, @@ -292,10 +292,10 @@ func TestColorForMethod(t *testing.T) { return p.MethodColor() } - assert.Equal(t, blue, colorForMethod("GET"), "get should be blue") - assert.Equal(t, cyan, colorForMethod("POST"), "post should be cyan") - assert.Equal(t, yellow, colorForMethod("PUT"), "put should be yellow") - assert.Equal(t, red, colorForMethod("DELETE"), "delete should be red") + assert.Equal(t, blue, colorForMethod(http.MethodGet), "get should be blue") + assert.Equal(t, cyan, colorForMethod(http.MethodPost), "post should be cyan") + assert.Equal(t, yellow, colorForMethod(http.MethodPut), "put should be yellow") + assert.Equal(t, red, colorForMethod(http.MethodDelete), "delete should be red") assert.Equal(t, green, colorForMethod("PATCH"), "patch should be green") assert.Equal(t, magenta, colorForMethod("HEAD"), "head should be magenta") assert.Equal(t, white, colorForMethod("OPTIONS"), "options should be white") @@ -369,15 +369,15 @@ func TestErrorLogger(t *testing.T) { c.String(http.StatusInternalServerError, "hola!") }) - w := PerformRequest(router, "GET", "/error") + w := PerformRequest(router, http.MethodGet, "/error") assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "{\"error\":\"this is an error\"}", w.Body.String()) - w = PerformRequest(router, "GET", "/abort") + w = PerformRequest(router, http.MethodGet, "/abort") assert.Equal(t, http.StatusUnauthorized, w.Code) assert.Equal(t, "{\"error\":\"no authorized\"}", w.Body.String()) - w = PerformRequest(router, "GET", "/print") + w = PerformRequest(router, http.MethodGet, "/print") assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, "hola!{\"error\":\"this is an error\"}", w.Body.String()) } @@ -389,11 +389,11 @@ func TestLoggerWithWriterSkippingPaths(t *testing.T) { router.GET("/logged", func(c *Context) {}) router.GET("/skipped", func(c *Context) {}) - PerformRequest(router, "GET", "/logged") + PerformRequest(router, http.MethodGet, "/logged") assert.Contains(t, buffer.String(), "200") buffer.Reset() - PerformRequest(router, "GET", "/skipped") + PerformRequest(router, http.MethodGet, "/skipped") assert.Contains(t, buffer.String(), "") } @@ -407,11 +407,11 @@ func TestLoggerWithConfigSkippingPaths(t *testing.T) { router.GET("/logged", func(c *Context) {}) router.GET("/skipped", func(c *Context) {}) - PerformRequest(router, "GET", "/logged") + PerformRequest(router, http.MethodGet, "/logged") assert.Contains(t, buffer.String(), "200") buffer.Reset() - PerformRequest(router, "GET", "/skipped") + PerformRequest(router, http.MethodGet, "/skipped") assert.Contains(t, buffer.String(), "") } @@ -427,11 +427,11 @@ func TestLoggerWithConfigSkipper(t *testing.T) { router.GET("/logged", func(c *Context) { c.Status(http.StatusOK) }) router.GET("/skipped", func(c *Context) { c.Status(http.StatusNoContent) }) - PerformRequest(router, "GET", "/logged") + PerformRequest(router, http.MethodGet, "/logged") assert.Contains(t, buffer.String(), "200") buffer.Reset() - PerformRequest(router, "GET", "/skipped") + PerformRequest(router, http.MethodGet, "/skipped") assert.Contains(t, buffer.String(), "") } diff --git a/middleware_test.go b/middleware_test.go index acdf89c42e..eafc60ad02 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -35,7 +35,7 @@ func TestMiddlewareGeneralCase(t *testing.T) { signature += " XX " }) // RUN - w := PerformRequest(router, "GET", "/") + w := PerformRequest(router, http.MethodGet, "/") // TEST assert.Equal(t, http.StatusOK, w.Code) @@ -71,7 +71,7 @@ func TestMiddlewareNoRoute(t *testing.T) { signature += " X " }) // RUN - w := PerformRequest(router, "GET", "/") + w := PerformRequest(router, http.MethodGet, "/") // TEST assert.Equal(t, http.StatusNotFound, w.Code) @@ -108,7 +108,7 @@ func TestMiddlewareNoMethodEnabled(t *testing.T) { signature += " XX " }) // RUN - w := PerformRequest(router, "GET", "/") + w := PerformRequest(router, http.MethodGet, "/") // TEST assert.Equal(t, http.StatusMethodNotAllowed, w.Code) @@ -149,7 +149,7 @@ func TestMiddlewareNoMethodDisabled(t *testing.T) { }) // RUN - w := PerformRequest(router, "GET", "/") + w := PerformRequest(router, http.MethodGet, "/") // TEST assert.Equal(t, http.StatusNotFound, w.Code) @@ -175,7 +175,7 @@ func TestMiddlewareAbort(t *testing.T) { }) // RUN - w := PerformRequest(router, "GET", "/") + w := PerformRequest(router, http.MethodGet, "/") // TEST assert.Equal(t, http.StatusUnauthorized, w.Code) @@ -196,7 +196,7 @@ func TestMiddlewareAbortHandlersChainAndNext(t *testing.T) { c.Next() }) // RUN - w := PerformRequest(router, "GET", "/") + w := PerformRequest(router, http.MethodGet, "/") // TEST assert.Equal(t, http.StatusGone, w.Code) @@ -219,7 +219,7 @@ func TestMiddlewareFailHandlersChain(t *testing.T) { signature += "C" }) // RUN - w := PerformRequest(router, "GET", "/") + w := PerformRequest(router, http.MethodGet, "/") // TEST assert.Equal(t, http.StatusInternalServerError, w.Code) @@ -246,7 +246,7 @@ func TestMiddlewareWrite(t *testing.T) { }) }) - w := PerformRequest(router, "GET", "/") + w := PerformRequest(router, http.MethodGet, "/") assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, strings.Replace("hola\nbar{\"foo\":\"bar\"}{\"foo\":\"bar\"}event:test\ndata:message\n\n", " ", "", -1), strings.Replace(w.Body.String(), " ", "", -1)) diff --git a/recovery_test.go b/recovery_test.go index ee063cd1dc..08eec1e40a 100644 --- a/recovery_test.go +++ b/recovery_test.go @@ -25,7 +25,7 @@ func TestPanicClean(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := PerformRequest(router, "GET", "/recovery", + w := PerformRequest(router, http.MethodGet, "/recovery", header{ Key: "Host", Value: "www.google.com", @@ -55,7 +55,7 @@ func TestPanicInHandler(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := PerformRequest(router, "GET", "/recovery") + w := PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Contains(t, buffer.String(), "panic recovered") @@ -66,7 +66,7 @@ func TestPanicInHandler(t *testing.T) { // Debug mode prints the request SetMode(DebugMode) // RUN - w = PerformRequest(router, "GET", "/recovery") + w = PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Contains(t, buffer.String(), "GET /recovery") @@ -83,7 +83,7 @@ func TestPanicWithAbort(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := PerformRequest(router, "GET", "/recovery") + w := PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) } @@ -134,7 +134,7 @@ func TestPanicWithBrokenPipe(t *testing.T) { panic(e) }) // RUN - w := PerformRequest(router, "GET", "/recovery") + w := PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, expectCode, w.Code) assert.Contains(t, strings.ToLower(buf.String()), expectMsg) @@ -155,7 +155,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := PerformRequest(router, "GET", "/recovery") + w := PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "panic recovered") @@ -166,7 +166,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) { // Debug mode prints the request SetMode(DebugMode) // RUN - w = PerformRequest(router, "GET", "/recovery") + w = PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "GET /recovery") @@ -190,7 +190,7 @@ func TestCustomRecovery(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := PerformRequest(router, "GET", "/recovery") + w := PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "panic recovered") @@ -201,7 +201,7 @@ func TestCustomRecovery(t *testing.T) { // Debug mode prints the request SetMode(DebugMode) // RUN - w = PerformRequest(router, "GET", "/recovery") + w = PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "GET /recovery") @@ -225,7 +225,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) { panic("Oupps, Houston, we have a problem") }) // RUN - w := PerformRequest(router, "GET", "/recovery") + w := PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "panic recovered") @@ -236,7 +236,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) { // Debug mode prints the request SetMode(DebugMode) // RUN - w = PerformRequest(router, "GET", "/recovery") + w = PerformRequest(router, http.MethodGet, "/recovery") // TEST assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, buffer.String(), "GET /recovery") diff --git a/render/render_test.go b/render/render_test.go index 27a5065be7..ad633b00b0 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -369,7 +369,7 @@ func TestRenderXML(t *testing.T) { } func TestRenderRedirect(t *testing.T) { - req, err := http.NewRequest("GET", "/test-redirect", nil) + req, err := http.NewRequest(http.MethodGet, "/test-redirect", nil) require.NoError(t, err) data1 := Redirect{ diff --git a/routes_test.go b/routes_test.go index 49f355a79d..995ff51cd8 100644 --- a/routes_test.go +++ b/routes_test.go @@ -523,8 +523,8 @@ func TestRouteNotAllowedEnabled3(t *testing.T) { w := PerformRequest(router, http.MethodPut, "/path") assert.Equal(t, http.StatusMethodNotAllowed, w.Code) allowed := w.Header().Get("Allow") - assert.Contains(t, allowed, "GET") - assert.Contains(t, allowed, "POST") + assert.Contains(t, allowed, http.MethodGet) + assert.Contains(t, allowed, http.MethodPost) } func TestRouteNotAllowedDisabled(t *testing.T) { @@ -557,7 +557,7 @@ func TestRouterNotFoundWithRemoveExtraSlash(t *testing.T) { {"/nope", http.StatusNotFound, ""}, // NotFound } for _, tr := range testRoutes { - w := PerformRequest(router, "GET", tr.route) + w := PerformRequest(router, http.MethodGet, tr.route) assert.Equal(t, tr.code, w.Code) if w.Code != http.StatusNotFound { assert.Equal(t, tr.location, w.Header().Get("Location")) @@ -786,6 +786,6 @@ func TestEngineHandleMethodNotAllowedCornerCase(t *testing.T) { v1.GET("/orgs/:id", handlerTest1) v1.DELETE("/orgs/:id", handlerTest1) - w := PerformRequest(r, "GET", "/base/v1/user/groups") + w := PerformRequest(r, http.MethodGet, "/base/v1/user/groups") assert.Equal(t, http.StatusNotFound, w.Code) } diff --git a/utils_test.go b/utils_test.go index af0899636d..8098c681af 100644 --- a/utils_test.go +++ b/utils_test.go @@ -29,7 +29,7 @@ type testStruct struct { } func (t *testStruct) ServeHTTP(w http.ResponseWriter, req *http.Request) { - assert.Equal(t.T, "POST", req.Method) + assert.Equal(t.T, http.MethodPost, req.Method) assert.Equal(t.T, "/path", req.URL.Path) w.WriteHeader(http.StatusInternalServerError) fmt.Fprint(w, "hello") @@ -39,17 +39,17 @@ func TestWrap(t *testing.T) { router := New() router.POST("/path", WrapH(&testStruct{t})) router.GET("/path2", WrapF(func(w http.ResponseWriter, req *http.Request) { - assert.Equal(t, "GET", req.Method) + assert.Equal(t, http.MethodGet, req.Method) assert.Equal(t, "/path2", req.URL.Path) w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, "hola!") })) - w := PerformRequest(router, "POST", "/path") + w := PerformRequest(router, http.MethodPost, "/path") assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, "hello", w.Body.String()) - w = PerformRequest(router, "GET", "/path2") + w = PerformRequest(router, http.MethodGet, "/path2") assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, "hola!", w.Body.String()) } @@ -119,13 +119,13 @@ func TestBindMiddleware(t *testing.T) { called = true value = c.MustGet(BindKey).(*bindTestStruct) }) - PerformRequest(router, "GET", "/?foo=hola&bar=10") + PerformRequest(router, http.MethodGet, "/?foo=hola&bar=10") assert.True(t, called) assert.Equal(t, "hola", value.Foo) assert.Equal(t, 10, value.Bar) called = false - PerformRequest(router, "GET", "/?foo=hola&bar=1") + PerformRequest(router, http.MethodGet, "/?foo=hola&bar=1") assert.False(t, called) assert.Panics(t, func() { From e46bd521859fdfc83c508f1d42c92cb7f91e9fcb Mon Sep 17 00:00:00 2001 From: haesuo566 <102643523+haesuo566@users.noreply.github.com> Date: Sat, 16 Nov 2024 00:54:06 +0900 Subject: [PATCH 343/346] refactor(context): add an optional permission parameter to the SaveUploadedFile method (#4068) (#4088) Co-authored-by: hso --- context.go | 13 +++++++++++-- context_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index cab14529b4..c724daf344 100644 --- a/context.go +++ b/context.go @@ -7,6 +7,7 @@ package gin import ( "errors" "io" + "io/fs" "log" "math" "mime/multipart" @@ -676,14 +677,22 @@ func (c *Context) MultipartForm() (*multipart.Form, error) { } // SaveUploadedFile uploads the form file to specific dst. -func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error { +func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string, perm ...fs.FileMode) error { src, err := file.Open() if err != nil { return err } defer src.Close() - if err = os.MkdirAll(filepath.Dir(dst), 0o750); err != nil { + if len(perm) <= 0 { + perm = append(perm, 0o750) + } + + if err = os.MkdirAll(filepath.Dir(dst), perm[0]); err != nil { + return err + } + + if err = os.Chmod(filepath.Dir(dst), perm[0]); err != nil { return err } diff --git a/context_test.go b/context_test.go index 91d5e898ff..5b63a647c9 100644 --- a/context_test.go +++ b/context_test.go @@ -11,12 +11,14 @@ import ( "fmt" "html/template" "io" + "io/fs" "mime/multipart" "net" "net/http" "net/http/httptest" "net/url" "os" + "path/filepath" "reflect" "strconv" "strings" @@ -155,6 +157,45 @@ func TestSaveUploadedCreateFailed(t *testing.T) { require.Error(t, c.SaveUploadedFile(f, "/")) } +func TestSaveUploadedFileWithPermission(t *testing.T) { + buf := new(bytes.Buffer) + mw := multipart.NewWriter(buf) + w, err := mw.CreateFormFile("file", "permission_test") + require.NoError(t, err) + _, err = w.Write([]byte("permission_test")) + require.NoError(t, err) + mw.Close() + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request.Header.Set("Content-Type", mw.FormDataContentType()) + f, err := c.FormFile("file") + require.NoError(t, err) + assert.Equal(t, "permission_test", f.Filename) + var mode fs.FileMode = 0o755 + require.NoError(t, c.SaveUploadedFile(f, "permission_test", mode)) + info, err := os.Stat(filepath.Dir("permission_test")) + require.NoError(t, err) + assert.Equal(t, info.Mode().Perm(), mode) +} + +func TestSaveUploadedFileWithPermissionFailed(t *testing.T) { + buf := new(bytes.Buffer) + mw := multipart.NewWriter(buf) + w, err := mw.CreateFormFile("file", "permission_test") + require.NoError(t, err) + _, err = w.Write([]byte("permission_test")) + require.NoError(t, err) + mw.Close() + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request.Header.Set("Content-Type", mw.FormDataContentType()) + f, err := c.FormFile("file") + require.NoError(t, err) + assert.Equal(t, "permission_test", f.Filename) + var mode fs.FileMode = 0o644 + require.Error(t, c.SaveUploadedFile(f, "test/permission_test", mode)) +} + func TestContextReset(t *testing.T) { router := New() c := router.allocateContext(0) From e2e80f33472bd02094f242da3c3efde2cec0a037 Mon Sep 17 00:00:00 2001 From: Xianglin Gao Date: Sat, 28 Dec 2024 17:18:03 +0800 Subject: [PATCH 344/346] chore(security): update vendor to fix CVE (#4121) Signed-off-by: Xianglin Gao --- go.mod | 8 ++++---- go.sum | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 035c2dea30..5de7d06525 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/quic-go/quic-go v0.43.1 github.com/stretchr/testify v1.9.0 github.com/ugorji/go/codec v1.2.12 - golang.org/x/net v0.27.0 + golang.org/x/net v0.33.0 google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -39,10 +39,10 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/crypto v0.25.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect ) diff --git a/go.sum b/go.sum index 55a21627f5..d0389bb714 100644 --- a/go.sum +++ b/go.sum @@ -92,22 +92,22 @@ go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= From 23d6961aeb9d2670a7b36c77cb180f479e220580 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 30 Dec 2024 11:39:24 +0800 Subject: [PATCH 345/346] ci(lint): update workflows and improve test request consistency (#4126) - Update GoReleaser action to version 6 in GitHub workflow - Use `http.MethodPost` constant in test requests instead of hardcoded string Signed-off-by: appleboy --- .github/workflows/goreleaser.yml | 2 +- context_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 8ae1182330..22edf45382 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -21,7 +21,7 @@ jobs: with: go-version: "^1" - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v5 + uses: goreleaser/goreleaser-action@v6 with: # either 'goreleaser' (default) or 'goreleaser-pro' distribution: goreleaser diff --git a/context_test.go b/context_test.go index 5b63a647c9..ef0cfccd82 100644 --- a/context_test.go +++ b/context_test.go @@ -166,7 +166,7 @@ func TestSaveUploadedFileWithPermission(t *testing.T) { require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") require.NoError(t, err) @@ -187,7 +187,7 @@ func TestSaveUploadedFileWithPermissionFailed(t *testing.T) { require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") require.NoError(t, err) From 3f818c3fa69e03feb46d2b49d2a8084c425cbed6 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 30 Dec 2024 11:40:37 +0800 Subject: [PATCH 346/346] chore(security): upgrade quic-go version to 0.48.2 (#4127) - Update Go versions in GitHub Actions workflow to `1.22` and `1.23` - Update README to require Go version `1.22` or above - Adjust table formatting in README for better alignment - Update warning message in `debug.go` to reflect Go version `1.22` - Update test in `debug_test.go` to reflect Go version `1.22` - Update `go.mod` to require Go version `1.22` - Update dependencies in `go.mod` to newer versions Signed-off-by: appleboy --- .github/workflows/gin.yml | 2 +- README.md | 4 ++-- debug.go | 2 +- debug_test.go | 2 +- go.mod | 9 ++++----- go.sum | 23 +++++++---------------- 6 files changed, 16 insertions(+), 26 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 74983c5006..1d193efb38 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -33,7 +33,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: ["1.21", "1.22"] + go: ["1.22", "1.23"] test-tags: ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json", "-race"] include: diff --git a/README.md b/README.md index 0464107c3a..ae155048c6 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ If you need performance and good productivity, you will love Gin. ### Prerequisites -Gin requires [Go](https://go.dev/) version [1.21](https://go.dev/doc/devel/release#go1.21.0) or above. +Gin requires [Go](https://go.dev/) version [1.22](https://go.dev/doc/devel/release#go1.22.0) or above. ### Getting Gin @@ -113,7 +113,7 @@ The documentation is also available on [gin-gonic.com](https://gin-gonic.com) in Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter), [see all benchmarks](/BENCHMARKS.md). | Benchmark name | (1) | (2) | (3) | (4) | -| ------------------------------ | ---------:| ---------------:| ------------:| ---------------:| +| ------------------------------ | --------: | --------------: | -----------: | --------------: | | BenchmarkGin_GithubAll | **43550** | **27364 ns/op** | **0 B/op** | **0 allocs/op** | | BenchmarkAce_GithubAll | 40543 | 29670 ns/op | 0 B/op | 0 allocs/op | | BenchmarkAero_GithubAll | 57632 | 20648 ns/op | 0 B/op | 0 allocs/op | diff --git a/debug.go b/debug.go index 62085c5dc5..43dcd722d3 100644 --- a/debug.go +++ b/debug.go @@ -78,7 +78,7 @@ func getMinVer(v string) (uint64, error) { func debugPrintWARNINGDefault() { if v, e := getMinVer(runtime.Version()); e == nil && v < ginSupportMinGoVer { - debugPrint(`[WARNING] Now Gin requires Go 1.21+. + debugPrint(`[WARNING] Now Gin requires Go 1.22+. `) } diff --git a/debug_test.go b/debug_test.go index 0efbfd7850..4b440e3a9f 100644 --- a/debug_test.go +++ b/debug_test.go @@ -106,7 +106,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { }) m, e := getMinVer(runtime.Version()) if e == nil && m < ginSupportMinGoVer { - assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.21+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) + assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.22+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } else { assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } diff --git a/go.mod b/go.mod index 5de7d06525..398787eae9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gin-gonic/gin -go 1.21.0 +go 1.22 require ( github.com/bytedance/sonic v1.11.6 @@ -10,7 +10,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.20 github.com/pelletier/go-toml/v2 v2.2.2 - github.com/quic-go/quic-go v0.43.1 + github.com/quic-go/quic-go v0.48.2 github.com/stretchr/testify v1.9.0 github.com/ugorji/go/codec v1.2.12 golang.org/x/net v0.33.0 @@ -29,18 +29,17 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect - github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qpack v0.5.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect diff --git a/go.sum b/go.sum index d0389bb714..ddd0f87d44 100644 --- a/go.sum +++ b/go.sum @@ -9,7 +9,6 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -44,10 +43,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -62,15 +57,12 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ= -github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= +github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -94,8 +86,8 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VA golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= @@ -114,9 +106,8 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=