From 01363191befaea052a8507ace95efdb70395960a Mon Sep 17 00:00:00 2001 From: jincheng9 Date: Sun, 2 Jan 2022 14:04:07 +0800 Subject: [PATCH 001/291] fix: typo (#3006) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 231bb142..cccb013a 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 002/291] 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 4d002c23..9e02aede 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 21c43d15..0aece61d 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 3f440bce..e74bddd5 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 80961ce1..ec5e6cdc 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 4b4afd4a..e0a756c3 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 d164bfa3..ac3669a4 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 d6d8b452..232f595b 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 ffe3469e..b3f0c47b 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 cc486c35..b50914f2 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 003/291] 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 05b9760f..118eb762 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 adacf5ee..c16d8c81 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 004/291] 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 118eb762..35488359 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 c16d8c81..59efbf75 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 005/291] 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 ed054bfd..0802e085 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 006/291] 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 cccb013a..d69df70b 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 007/291] fix typo (#3044) --- tree.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tree.go b/tree.go index a30d3496..18e50ee3 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 008/291] 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 c25a9091..f3927b3d 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 009/291] 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 00000000..4a081e0c --- /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 35488359..b088f26b 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 010/291] 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 9bd4c46a..b54dbd8a 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 0aece61d..629a109b 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 b088f26b..b59e5907 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 59efbf75..c01ba524 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 011/291] 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 629a109b..ae1762ef 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 012/291] Update routergroup.go (#3056) --- routergroup.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routergroup.go b/routergroup.go index 27d7aad6..b84fcca3 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 013/291] 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 b59e5907..8087227e 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 c01ba524..7f892453 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 014/291] 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 b75df63f..d3fa0757 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 015/291] 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 4a081e0c..4cbc4554 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 f3927b3d..5b818a9e 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 016/291] Distinguish between group and nested group (#3083) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d3fa0757..2d113532 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 017/291] 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 d69df70b..48d75506 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 9e02aede..4eed164a 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 c32f0eeb..e4599ea9 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 b50914f2..d2a740bf 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 018/291] 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 2d113532..4b4d236b 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 b84fcca3..3fba3a91 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 232f595b..c1fad3a9 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 b3f0c47b..4a0cb493 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 019/291] 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 4a0cb493..56430970 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 020/291] fix: typo (#3086) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b4d236b..9a9785e6 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 021/291] 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 5b818a9e..1aecda58 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 022/291] 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 18e50ee3..88100eec 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 023/291] 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 8087227e..7a9249c6 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 7f892453..6006ffb9 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 024/291] 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 00000000..a0104dc8 --- /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 00000000..1331d390 --- /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 0414a345..703a1cf8 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 f0b667b2..b3818549 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 5b0ce39d..b1edbf5a 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 bd8764b1..3515a8cc 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 ff130102..df7742b7 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 fa2a6540..f5cbf57b 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 fa7ad1bc..c24dd553 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 516554eb..78f4df0e 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 b99302af..14f525ef 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 45aaa494..2e3e1dd5 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 2a442996..65197713 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 75600ba8..11561c84 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 4aaa60be..545914d0 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 a4e47153..ace8f654 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 219743f2..9790ce6a 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 a3c0df51..4d44ab98 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 5299fbf6..d05c891e 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 4e901149..e62af4a6 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 a2d36d6a..183f141e 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 48d75506..faa48133 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 4eed164a..fb46e679 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 ed313868..badcb5e1 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 ab447429..004e13db 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 3418cbc8..578e857d 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 9a17d859..ac72dc42 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 b54dbd8a..b0e0154f 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 ae1762ef..0c11134f 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 61b84546..ab43d112 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 ec5e6cdc..da1b654e 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 40eba3b2..3efe146b 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 ac3669a4..2c327a65 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 00000000..57349be3 --- /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 6696ece9..bdfaf11a 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 3ebcee97..0a7dcef2 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 7f17ca4d..2c0e9aa8 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 1d2aa871..2e57f5ca 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 d16cf6e6..7b3601a0 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 e417731a..8b28dc3f 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 461b720a..b77a776b 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 cc5390a2..c396a5a1 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 0df78360..0fc7a665 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 00000000..f16864c5 --- /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 bf45e028..6687aae2 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 94c53386..085b5803 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 e4599ea9..b41a3b0f 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 025/291] 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 1aecda58..fa9d68b3 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 9a9785e6..4aa638d6 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 badcb5e1..d8d6c8e1 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 05509992..7c54444a 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 026/291] 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 fa9d68b3..c432a337 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 7a9249c6..dcd83686 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 6006ffb9..c0980ad5 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 027/291] 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 b0e0154f..c2e95f29 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 ab43d112..1f9d63ae 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 26826689..7f9095f0 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 028/291] 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 c432a337..f67641a9 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 029/291] 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 dcd83686..846bd5df 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 c0980ad5..c92ce01a 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 030/291] 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 846bd5df..6c46755d 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 c92ce01a..5108a599 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 031/291] 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 6c46755d..bfb462b8 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 5108a599..983b50a4 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 032/291] docs: Update README.md (#3108) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4aa638d6..af9a1e53 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 033/291] 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 faa48133..0fe13331 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 034/291] 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 f67641a9..771c0b9b 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 035/291] 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 771c0b9b..c5e2744d 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 036/291] 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 8c22e7bd..0dfa9032 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 0c11134f..a4380622 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 da1b654e..b7049988 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 8bc75703..f3f530a4 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 037/291] 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 b1edbf5a..d9746e26 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 e74bddd5..5fe65a4b 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 038/291] use StringToBytes func (#2798) --- auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth.go b/auth.go index 4d8a6ce4..482c499a 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 039/291] 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 0fe13331..5e53aaf0 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 fb46e679..20038e93 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 040/291] 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 bfb462b8..2da1fe35 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 983b50a4..d7a221f1 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 041/291] 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 4cbc4554..e27022d1 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 042/291] 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 1fb994b4..4e7d734a 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 1b5fb2ff..6fd9a137 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 043/291] 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 c5e2744d..e8ef30ca 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 044/291] 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 c2e95f29..3a831e5b 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 045/291] 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 00000000..eca089ce --- /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 046/291] [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 af9a1e53..6b4cabb4 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 703a1cf8..50510514 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 b3818549..7f6a904a 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 d9746e26..f08e173f 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 00000000..5b9ad165 --- /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 00000000..2bc0e3a4 --- /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 5e53aaf0..ecbd3dac 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 20038e93..7e4d0b3f 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 2da1fe35..e519b36e 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 d7a221f1..d7555f9a 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 bcd568bf..1fa48061 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 00000000..1192c78c --- /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 047/291] 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 a0104dc8..42b1ea46 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 482c499a..2503c515 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 e44bd100..42b6f8fd 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 0b3f82df..5b7929b8 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 1331d390..d8251a7c 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 50510514..a58924ed 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 f08e173f..f0996216 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 3515a8cc..c03afe75 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 8d628369..9292e2aa 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 f5cbf57b..b17352ba 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 c24dd553..98cebfec 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 9572ea03..5788133f 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 14f525ef..03bc78da 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 2e3e1dd5..36eb27a3 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 65197713..d1f035e4 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 c4d7ed74..4ebe8326 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 545914d0..99328603 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 ace8f654..44f2fdb9 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 9790ce6a..c958b88b 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 5b9ad165..83b27267 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 d05c891e..801bd9b7 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 e62af4a6..a70f4ad3 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 183f141e..b0d36a35 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 ecbd3dac..6b25b3a3 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 053e6c5a..26760507 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 431d54c7..69c97864 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 8bf93896..931313f6 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 eca089ce..00000000 --- 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 7e4d0b3f..f9f0c1dc 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 d8d6c8e1..25fd7c87 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 7c54444a..4ac55fe1 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 004e13db..fdad8554 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 f8df651c..0240b2ec 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 578e857d..2853ce8e 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 ac72dc42..78d561c6 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 e5f3d602..64274735 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 3a831e5b..e516360c 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 0802e085..1550b868 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 0dfa9032..b0532a25 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 a4380622..02f23247 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 5fe65a4b..c6350e81 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 e519b36e..6cdde34e 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 d7555f9a..d06b8103 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 da960571..23f71726 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 75b60224..a26d7db2 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 232f8dca..853b1a90 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 1f9d63ae..cd1e7fa6 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 b7049988..fa0d9ce8 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 e0a756c3..a235fe91 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 4e7d734a..545fdaaf 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 6fd9a137..2407f463 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 3efe146b..abb64510 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 2c327a65..347917e7 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 57349be3..b19ad45d 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 6ba657ba..a653ea30 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 bdfaf11a..c308408d 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 0a7dcef2..af678e80 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 2c0e9aa8..e0f30f7a 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 2e57f5ca..9331c405 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 d5282e49..5752d8d8 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 3930f51d..aaceb9ea 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 c006691c..70e3a47e 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 1fa48061..7955000c 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 8b28dc3f..a13fff42 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 b77a776b..77eafdfd 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 1192c78c..40f044c8 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 c396a5a1..6af89017 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 0fc7a665..4f0ac01f 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 7f9095f0..77c7ed8f 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 9061d021..57d163c9 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 3fba3a91..3c082d93 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 c1fad3a9..41f96372 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 56430970..d7034b22 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 3a7a5ddf..b3be93b4 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 f16864c5..2203f33a 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 b41a3b0f..4021a2ab 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 d2a740bf..058ddb9d 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 4b69b9b9..40e6505b 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 048/291] 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 00000000..e0f6e4f6 --- /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 00000000..e435e56a --- /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 40e6505b..9b0eae22 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 049/291] 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 533204ed..b4773ef3 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 - - -**@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 4c806a5a..6d540289 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 83b27267..a6b8a90a 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 4d44ab98..29151064 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 7b3601a0..64212361 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 050/291] 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 e0f6e4f6..64ed8b2b 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 051/291] 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 6d540289..1e7e83ee 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 052/291] fix: typo (#3171) --- README.md | 75 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 6b4cabb4..5cc8321b 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 053/291] 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 6b25b3a3..b1ad95e6 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 f9f0c1dc..6c0be544 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 e516360c..f9324299 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 054/291] 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 1e7e83ee..1bc51a8c 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 9b0eae22..632ca7d1 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 055/291] 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 6cdde34e..2d2c7dcb 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 d06b8103..18233de4 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 056/291] 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 b1ad95e6..46bf1133 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 057/291] 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 2d2c7dcb..90103463 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 18233de4..620eccec 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 058/291] 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 88100eec..956bf4dd 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 059/291] 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 90103463..f7590661 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 620eccec..a590d38f 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 060/291] 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 e8ef30ca..6dc787a2 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 5cc8321b..2477d0b0 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 25fd7c87..8367de9c 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 4ac55fe1..5c29a74b 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 061/291] 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 f7590661..283c6a50 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 a590d38f..4d6c06ea 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 062/291] 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 8367de9c..b9f8234a 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 5c29a74b..bf0e6ab8 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 063/291] 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 6c0be544..d09b0ae1 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 064/291] 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 283c6a50..6371d226 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 4d6c06ea..aba00199 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 065/291] 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 6371d226..ae7a5f81 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 aba00199..3529342c 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 066/291] 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 ae7a5f81..0b259e13 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 3529342c..640ed10e 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 067/291] docs: update markdown format (#3260) Signed-off-by: Bo-Yi Wu --- README.md | 1786 +++++++++++++++++++++++++++-------------------------- 1 file changed, 901 insertions(+), 885 deletions(-) diff --git a/README.md b/README.md index 2477d0b0..1c315f88 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[]"] + 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) + 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") + // 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() + router := gin.Default() - // Simple group: v1 - v1 := router.Group("/v1") - { - v1.POST("/login", loginEndpoint) - v1.POST("/submit", submitEndpoint) - v1.POST("/read", readEndpoint) - } + // 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() + // 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()) + // 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()) + // 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) + // 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) + // 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) - } + // 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() + // 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()) + // 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) - })) + // 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() + 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 - } + // 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 - } + 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"}) - }) + 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 - } + // 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 - } + 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"}) - }) + 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 - } + // 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 - } + 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"}) - }) + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) - // Listen and serve on 0.0.0.0:8080 - router.Run(":8080") + // Listen and serve on 0.0.0.0:8080 + router.Run(":8080") } ``` -**Sample request** -```shell +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() + 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}) - }) + // 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("/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("/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("/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) - }) + 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") + // 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() + 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", - })) + // 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 :("}) - } - }) + // /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") + // 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() + router := gin.Default() - s := &http.Server{ - Addr: ":8080", - Handler: router, - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - MaxHeaderBytes: 1 << 20, - } - s.ListenAndServe() + 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, - } + 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, - } + 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 := 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 - }) + 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) - } + 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" + "context" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" - "github.com/gin-gonic/gin" + "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") - }) + 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, - } + 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) - } - }() + // 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...") + // 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() + // 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) - } + if err := srv.Shutdown(ctx); err != nil { + log.Fatal("Server forced to shutdown:", err) + } - log.Println("Server exiting") + 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 := 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.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 068/291] 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 6dc787a2..cb3a49a8 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 5d55b444..ebde4ee8 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 1c315f88..8d7b5ec4 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 0b259e13..ed6402e3 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 640ed10e..9b4fb91e 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 a26d7db2..c5f3efc8 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 00000000..5a9ca4b2 --- /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 069/291] 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 ed6402e3..40f874ea 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 9b4fb91e..30dc0ce4 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 070/291] 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 40f874ea..924cefc3 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 30dc0ce4..58be7b01 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 071/291] 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 cb3a49a8..cff8f4a6 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 46bf1133..f9489a77 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 d09b0ae1..b3e81c14 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 2853ce8e..ca2bfc3f 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 78d561c6..f77a6342 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 1550b868..ea38c613 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 fa0d9ce8..7bc11371 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 a235fe91..acdf89c4 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 545fdaaf..fd26d907 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 d42d6b9d..82438c13 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 abb64510..05b30d95 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 3c082d93..2474a81c 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 072/291] 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 cff8f4a6..14de5f72 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 073/291] 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 924cefc3..54cc5523 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 58be7b01..b88e4a82 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 074/291] 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 14de5f72..004f9b9d 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 075/291] 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 2474a81c..8b877eda 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 076/291] 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 54cc5523..885ff0ee 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 b88e4a82..8b560189 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 077/291] 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 885ff0ee..daf52e2a 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 8b560189..4d832731 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 078/291] 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 69c97864..23377ffd 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 02f23247..4fac677a 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 079/291] 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 8b877eda..27308bc8 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 080/291] 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 daf52e2a..fa1db99a 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 4d832731..1c31d299 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 081/291] 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 64ed8b2b..654585b9 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 082/291] 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 004f9b9d..686d08a3 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 fa1db99a..1c64cf1b 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 1c31d299..35bfd2e9 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 083/291] 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 8d7b5ec4..960c66dc 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 b9f8234a..cbcedbc9 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 bf0e6ab8..abe8b41c 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 084/291] =?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 686d08a3..18b36d0e 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 23377ffd..0f8527fe 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 00000000..4b34ea24 --- /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 085/291] 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 98cebfec..540bbbb8 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 086/291] 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 f9489a77..b66b8adc 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 087/291] 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 f0996216..eae27802 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 78f4df0e..93d6a92f 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 a13fff42..3509db3c 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 088/291] 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 c03afe75..e216b854 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 b3e81c14..cc55cb01 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 ca2bfc3f..06b53c28 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 f9324299..a2e2e67c 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 77c7ed8f..43e828d7 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 27308bc8..dfbdd7b8 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 089/291] 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 956bf4dd..3f34b8ee 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 085b5803..2005738e 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 090/291] 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 1bc51a8c..a682c8c6 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 960c66dc..760bde02 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 091/291] 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 cc55cb01..85e0a616 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 a2e2e67c..35159d03 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 b3be93b4..7508c5c9 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 092/291] 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 760bde02..8a6b262a 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 093/291] 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 8a6b262a..6445e97d 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 094/291] 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 4fac677a..5ab2430c 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 095/291] 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 1c64cf1b..ebc1bd1a 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 35bfd2e9..acdf805d 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 096/291] 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 18b36d0e..45ae932e 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 097/291] 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 b66b8adc..ac9db17e 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 098/291] 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 6445e97d..48a0a13c 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 099/291] 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 48a0a13c..65492e52 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 100/291] 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 eae27802..9af4f88a 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 99328603..4e97c0f0 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 44f2fdb9..57721fc9 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 ac9db17e..c41c71ec 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 b0532a25..02b96221 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 5ab2430c..8825ac7e 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 05b30d95..3a90f250 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 d7034b22..cd8cf141 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 101/291] 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 65492e52..103a53c7 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 102/291] 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 45ae932e..7a4e61c6 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 103/291] 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 ebc1bd1a..200a4403 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 acdf805d..574e4a9a 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 104/291] 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 c41c71ec..737e4d7a 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 105/291] 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 654585b9..3af3a455 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 106/291] 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 200a4403..1929c959 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 574e4a9a..054058aa 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 107/291] 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 1929c959..7a355e98 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 054058aa..516c053c 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 108/291] 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 a682c8c6..2ab56179 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 632ca7d1..37e27f27 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 109/291] 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 3a90f250..2955c03a 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 110/291] 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 3509db3c..c5c5375f 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 111/291] 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 7a355e98..e9698339 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 516c053c..6d8df64a 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 112/291] docs(readme): move more example to docs/doc.md (#3449) --- README.md | 2344 ++------------------------------------------------- docs/doc.md | 2246 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2317 insertions(+), 2273 deletions(-) create mode 100644 docs/doc.md diff --git a/README.md b/README.md index 103a53c7..eccf814c 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 + +- **[Go](https://go.dev/)**: ~~any one of the **three latest major** [releases](https://go.dev/doc/devel/release)~~ (now version **1.16+** is required). + +### Getting Gin + +With [Go module](https://github.com/golang/go/wiki/Modules) support, simply add the following import -```sh -go get -u github.com/gin-gonic/gin ``` - -2. Import it in your code: - -```go import "github.com/gin-gonic/gin" ``` -3. (Optional) Import `net/http`. This is required for example if using constants such as `http.StatusOK`. +to your code, and then `go [build|run|test]` will automatically fetch the necessary dependencies. -```go -import "net/http" -``` - -## 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 ``` +### 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. + + +## 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](/BENCHMARKS.md) +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,2193 +154,11 @@ 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. +## Middlewares -## Build with json replacement +You can find many useful Gin middlewares at [gin-contrib](https://github.com/gin-contrib). -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()) -} -``` ## Users @@ -2380,3 +171,10 @@ 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. + + +## 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. \ No newline at end of file diff --git a/docs/doc.md b/docs/doc.md new file mode 100644 index 00000000..008a91db --- /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 113/291] 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 43e828d7..5cec1f66 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 57d163c9..6fa5ec71 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 114/291] 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 737e4d7a..b1352b9b 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 115/291] 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 b0d36a35..2535f8c3 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 e9698339..2b7a98cc 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 6d8df64a..a4f0f387 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 c5c5375f..ebb7d414 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 4f0ac01f..fc927c1f 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 116/291] 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 2b7a98cc..daff61de 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 a4f0f387..c7e0b38a 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 117/291] 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 daff61de..8cb21f9c 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 c7e0b38a..daf51f7d 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 118/291] 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 008a91db..7cebab56 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 119/291] 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 cd8cf141..ada8e1e4 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 3f34b8ee..dda8f4f7 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 120/291] 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 8cb21f9c..0b1d3a6b 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 daf51f7d..f86942b5 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 121/291] 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 0b1d3a6b..c498b482 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 f86942b5..409d393b 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 122/291] 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 b1352b9b..04742527 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 85e0a616..827ee0fa 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 123/291] 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 c498b482..e03001ec 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 409d393b..4e4e9632 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 124/291] 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 a6b8a90a..a66b93aa 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 125/291] 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 eccf814c..336155a9 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 126/291] 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 abe8b41c..ce8b19da 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 c6350e81..9276bed5 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 7bc11371..5f78708f 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 347917e7..fa8ab894 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 127/291] 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 7a4e61c6..9bd2698c 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 128/291] 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 e03001ec..ea2c94ac 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 4e4e9632..09341d45 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 129/291] 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 dfbdd7b8..c833fe8f 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 41f96372..6848063e 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 130/291] 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 ea2c94ac..4ec53257 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 09341d45..b2d2f736 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 131/291] 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 04742527..556f8ac9 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 827ee0fa..1ab6b339 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 af678e80..fc8dea45 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 ebb7d414..19255251 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 132/291] =?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 9bd2698c..fac97d47 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 4ec53257..51353f54 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 b2d2f736..01f94952 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 133/291] 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 5cec1f66..753a0b09 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 6fa5ec71..9fd5e87c 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 134/291] 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 51353f54..2ef45cfe 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 01f94952..b47804fc 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 135/291] 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 35159d03..32dae249 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 ada8e1e4..5310caec 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 136/291] 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 2ef45cfe..f7e28d8c 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 b47804fc..814f4eb3 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 137/291] 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 32dae249..f95e5dda 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 5310caec..633c0aba 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: "#bug"}) - assert.Equal(t, "%23bug/%2523bug/path", w.Header().Get("Location")) + 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: "/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 138/291] 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 2ab56179..cf24ec28 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 f7e28d8c..db36337e 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 814f4eb3..8bdb934a 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 37e27f27..390da4f3 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 139/291] 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 db36337e..3ec47800 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 8bdb934a..d6a91933 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 140/291] 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 3ec47800..da978740 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 d6a91933..cab49ab0 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 141/291] 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 fac97d47..5c1504a9 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 3af3a455..baf02af5 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 336155a9..cba54ab8 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 42b1ea46..00000000 --- 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 d8251a7c..00000000 --- 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 a58924ed..40948529 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 04d94079..a6cd6aa8 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 7f6a904a..93ad8ba3 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 36eb27a3..e21c2ee3 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 d1f035e4..22de9b55 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 11561c84..df386a6d 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 556f8ac9..5716318e 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 0f8527fe..00000000 --- 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 26760507..6118beaa 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 4b34ea24..dd75325b 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 931313f6..96b339c4 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 1ab6b339..1dec902c 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 cbcedbc9..1fc0cafe 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 ce8b19da..2d5e9a56 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 fdad8554..9521308f 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 7cebab56..e48c2ba1 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 23f71726..47c35598 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 c5f3efc8..c7ee83eb 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 853b1a90..45ed16ba 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 5a9ca4b2..529e16d0 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 b19ad45d..00000000 --- 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 e0f30f7a..d1d8e84b 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 64212361..db4b71e5 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 2203f33a..00000000 --- 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 4021a2ab..47106a7a 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 142/291] 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 da978740..d52e73cb 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 cab49ab0..bb8225b3 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 143/291] 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 d52e73cb..484e388b 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 bb8225b3..6193e008 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 144/291] 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 2955c03a..037be51a 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 145/291] 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 484e388b..0358006d 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 86e4c4d4..669c9c91 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 00000000..5b6040a6 --- /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 146/291] 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 f95e5dda..ed8b6dad 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 147/291] 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 5c1504a9..b758c7fa 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 c5e1de38..91dae02c 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 cba54ab8..e007bf2f 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 c9c9a522..00000000 --- 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 64274735..f17d7434 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 037be51a..515f9d2a 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 148/291] 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 0358006d..5fa62004 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 6193e008..90704f4e 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 149/291] 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 19255251..86dc362d 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 150/291] 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 5fa62004..3f3aa0eb 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 90704f4e..84502906 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 151/291] 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 6f8288d5..864787ca 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 152/291] 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 3f3aa0eb..7ec3e4fd 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 84502906..36d6b84e 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 153/291] 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 b758c7fa..df6e194e 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 baf02af5..5b205bab 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 154/291] 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 5716318e..cb360879 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 1dec902c..18051235 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 155/291] 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 cb360879..420ff167 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 18051235..70d47583 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 156/291] 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 cf24ec28..79685205 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 7ec3e4fd..e37698ea 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 36d6b84e..0a91a3e6 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 390da4f3..85462e55 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 157/291] 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 5b205bab..07a05483 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 158/291] 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 e37698ea..ded1334a 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 159/291] 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 ded1334a..e1295485 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 0a91a3e6..147a110a 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 160/291] 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 e1295485..b133475d 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 147a110a..a2d587ab 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 161/291] 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 86dc362d..c9db635f 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 9fd5e87c..964aa307 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 162/291] 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 df6e194e..54d76bb4 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 163/291] 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 b133475d..5c2ec05d 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 a2d587ab..b992f594 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 164/291] 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 e27022d1..b717a003 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 54d76bb4..645616bc 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 07a05483..40609266 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 165/291] 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 cd1e7fa6..1e6cf77a 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 5f78708f..b93e1e04 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 166/291] 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 5c2ec05d..7ebadde7 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 b992f594..75848b48 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 167/291] 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 7ebadde7..c365e77d 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 75848b48..4bd36dbb 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 168/291] docs: remove redundant comments (#3765) --- gin.go | 1 - 1 file changed, 1 deletion(-) diff --git a/gin.go b/gin.go index ed8b6dad..5a605cf1 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 169/291] 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 540bbbb8..55435b94 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 93d6a92f..acea8f77 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 170/291] 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 633c0aba..7a51f817 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 dda8f4f7..7b1e008b 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 2005738e..aacc914c 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 171/291] 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 645616bc..5f8c0c08 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 40609266..5364a913 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 172/291] 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 5364a913..01803232 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 173/291] 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 c365e77d..6b348fe2 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 4bd36dbb..39d74fc9 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 174/291] 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 b717a003..858124e1 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 5f8c0c08..2149a21e 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 01803232..cbd5d418 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 175/291] 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 858124e1..9a4c40d7 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 176/291] 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 6b348fe2..7f560453 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 39d74fc9..f4bf8ff0 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 177/291] 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 2149a21e..4ec95435 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 91dae02c..4a72f734 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 f4bf8ff0..8c4ef3fb 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 7b1e008b..45646461 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 178/291] 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 4ec95435..b36e1010 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 7f560453..0b60c5d7 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 8c4ef3fb..e360d9d2 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 179/291] 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 | 61 +++++++++++++++++++++++++++++--------------------- logger_test.go | 20 +++++++++++++++++ 3 files changed, 93 insertions(+), 26 deletions(-) diff --git a/docs/doc.md b/docs/doc.md index e48c2ba1..520d105c 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 1e6cf77a..db2c6832 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) - - param.ClientIP = c.ClientIP() - param.Method = c.Request.Method - param.StatusCode = c.Writer.Status() - param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String() - - param.BodySize = c.Writer.Size() - - if raw != "" { - path = path + "?" + raw - } - - param.Path = path - - fmt.Fprint(out, formatter(param)) + // Log only when it is not being skipped + if _, ok := skip[path]; ok || (conf.Skip != nil && conf.Skip(c)) { + return } + + param := LogFormatterParams{ + Request: c.Request, + isTerm: isTerm, + Keys: c.Keys, + } + + // Stop timer + param.TimeStamp = time.Now() + param.Latency = param.TimeStamp.Sub(start) + + param.ClientIP = c.ClientIP() + param.Method = c.Request.Method + param.StatusCode = c.Writer.Status() + param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String() + + param.BodySize = c.Writer.Size() + + if raw != "" { + path = path + "?" + raw + } + + param.Path = path + + fmt.Fprint(out, formatter(param)) } } diff --git a/logger_test.go b/logger_test.go index b93e1e04..6c1814dc 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 180/291] 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 45646461..878023d1 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 aacc914c..c9b03130 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 181/291] 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 55435b94..77a1bde6 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 acea8f77..16527eb9 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 182/291] 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 b36e1010..75e6d05d 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 183/291] 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 5a605cf1..b6ac5353 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 7a51f817..a0ff695f 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 184/291] 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 e216b854..ac43d7cc 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 801bd9b7..1fc15ff0 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 185/291] 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 70d47583..88165c07 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 520d105c..df006e87 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 b6ac5353..24a9864a 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 186/291] 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 75e6d05d..9ab00ae3 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 cbd5d418..8ae11823 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 187/291] 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 9ab00ae3..3fe007f1 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 0b60c5d7..fbbce7c0 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 e360d9d2..ce6c7fe7 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 188/291] 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 420ff167..126d35db 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 189/291] 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 a0ff695f..185abd99 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 190/291] 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 40948529..036b329b 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 93ad8ba3..552a86b2 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 9af4f88a..feb8eed5 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 88165c07..d060ccf0 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 c9db635f..145f1316 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 fc927c1f..042bb821 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 191/291] 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 185abd99..73f393e7 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 192/291] 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 126d35db..609827dd 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 d060ccf0..ac766e2b 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 193/291] 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 7955000c..4bdcfa23 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 194/291] test(git): gitignore add develop tools (#3370) --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index bdd50c95..1ea0e2b9 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 195/291] 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 2503c515..cc6c5a7e 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 42b6f8fd..f7175929 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 196/291] 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 609827dd..59abee17 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 197/291] 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 59abee17..a17a58e9 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 198/291] 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 ac766e2b..33cc43f9 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 24a9864a..1633fe13 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 8825ac7e..4550a7e5 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 199/291] 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 1fc0cafe..1761fe32 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 200/291] 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 a17a58e9..0c73a49f 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 201/291] 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 33cc43f9..089047c2 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 202/291] docs(middleware): comments to function `BasicAuthForProxy` (#3881) --- auth.go | 1 + 1 file changed, 1 insertion(+) diff --git a/auth.go b/auth.go index cc6c5a7e..2ed33ac0 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 203/291] 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 9521308f..b4c6cd88 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 204/291] 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 2ed33ac0..5d3222d5 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 205/291] 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 caefd63a..864302f4 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 206/291] 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 fbbce7c0..11ce23e8 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 ce6c7fe7..49eae343 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 207/291] 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 0c73a49f..3a9608d5 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 089047c2..9c1717ed 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 208/291] 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 00000000..47782e50 --- /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 209/291] 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 c958b88b..baa4aead 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 00000000..72102040 --- /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 210/291] 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 baa4aead..c958b88b 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 72102040..00000000 --- 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 211/291] 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 036b329b..94723879 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 212/291] 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 3a9608d5..afc3c353 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 9c1717ed..e9bbae52 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 df006e87..70c9f275 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 213/291] 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 afc3c353..391adafe 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 214/291] ci(Makefile): vet command add .PHONY (#3915) --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index ebde4ee8..b58f24f3 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 215/291] 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 e435e56a..1cc0bee1 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 216/291] 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 11ce23e8..13342ac2 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 49eae343..7a3aa22c 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 217/291] 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 1cc0bee1..ac2b462b 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 218/291] 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 3fe007f1..2e434341 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 219/291] 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 13342ac2..44e02a30 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 7a3aa22c..2028e88e 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 220/291] 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 77a1bde6..db235e56 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 16527eb9..ed01a086 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 70c9f275..177c4471 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 4550a7e5..e68f1ce8 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 221/291] 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 44e02a30..3e94e505 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 2028e88e..ce905e70 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 222/291] 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 ac2b462b..99b66fee 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 223/291] 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 85462e55..93ad9654 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 224/291] 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 79685205..de47c750 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 225/291] 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 1633fe13..8f323788 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 226/291] 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 db235e56..108606fa 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 227/291] 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 8f323788..03edbfff 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 228/291] 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 1761fe32..0d808f10 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 fd26d907..13aa3be0 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 2407f463..be03a9d0 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 229/291] 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 2e434341..4a9dbc93 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 6118beaa..00000000 --- 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 dd75325b..00000000 --- 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 e9bbae52..ae34c659 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 0d808f10..ae346e9c 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 2d5e9a56..e3909729 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 5b6040a6..a02c53c3 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 669c9c91..00000000 --- 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 230/291] feat(fs): Export, test and document OnlyFilesFS (#3939) --- fs.go | 52 ++++++++++++++++++++---------------- fs_test.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ routergroup.go | 2 +- 3 files changed, 101 insertions(+), 24 deletions(-) create mode 100644 fs_test.go diff --git a/fs.go b/fs.go index f17d7434..51c3db86 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 { - http.File -} +// 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) -// 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. -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 + + return neutralizedReaddirFile{f}, nil } -// Readdir overrides the http.File default implementation. -func (f neuteredReaddirFile) Readdir(_ int) ([]os.FileInfo, error) { +// neutralizedReaddirFile wraps http.File with a specific implementation of `Readdir`. +type neutralizedReaddirFile struct { + http.File +} + +// 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{FileSystem: fs} +} diff --git a/fs_test.go b/fs_test.go new file mode 100644 index 00000000..a1690cd9 --- /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 c833fe8f..b2540ec1 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 231/291] 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 4a9dbc93..61210772 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 4a72f734..5a65972a 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 03edbfff..57f8c2a3 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 02b96221..2125df92 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 3e94e505..6063f3bc 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 ce905e70..44af4cc1 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 232/291] 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 61210772..947abf9c 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 e007bf2f..04217d64 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 ae346e9c..62085c5d 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 e3909729..1e576681 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 233/291] 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 6063f3bc..4937d2b7 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 234/291] ci(Makefile): added help and descriptions to targets (#3964) --- Makefile | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index b58f24f3..1a7de86b 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: - @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - $(GO) get -u github.com/client9/misspell/cmd/misspell; \ - fi - misspell -error $(GOFILES) - .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 -w $(GOFILES) +.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 -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 235/291] 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 94723879..702d0e82 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 552a86b2..c8e61310 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 feb8eed5..c59e5e93 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 00000000..3b250bb0 --- /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 391adafe..35250667 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 ae34c659..36d6e34a 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 236/291] 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 04217d64..a595656c 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 237/291] 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 | 31 +++++++++------------ binding/default_validator_benchmark_test.go | 12 +++++--- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/binding/default_validator.go b/binding/default_validator.go index ac43d7cc..44b7a2ac 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()) - } - } - } - return b.String() } + + 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() } var _ StructValidator = (*defaultValidator)(nil) diff --git a/binding/default_validator_benchmark_test.go b/binding/default_validator_benchmark_test.go index 9292e2aa..44547412 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 238/291] 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 35250667..cd2645a9 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 36d6e34a..517f73e2 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 239/291] 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 cd2645a9..d2d5497e 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 517f73e2..b700389a 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 240/291] 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 d2d5497e..baa4b0f9 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 b700389a..8bbf2700 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 241/291] 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 108606fa..33389b28 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 ed01a086..afd51f9d 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 242/291] 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 878023d1..ce0f065c 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 243/291] 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 57f8c2a3..5ba1cf63 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 2125df92..53982712 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 ce0f065c..b0a5f982 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 c9b03130..3aa3a594 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 244/291] 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 a595656c..faeb4952 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 245/291] 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 177c4471..51366409 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 246/291] 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 5ba1cf63..48cc15c9 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 e68f1ce8..db70a8c5 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 247/291] 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 5a65972a..8d58c989 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 a6cd6aa8..a8116391 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 c59e5e93..2036b59b 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 afd51f9d..9ea0895c 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 4e97c0f0..9782b81d 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 1fc15ff0..c9bbe601 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 8bbf2700..66190b30 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 1e576681..edf4bb12 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 f77a6342..72a36992 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 a1690cd9..167ac1af 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 53982712..3082bc2c 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 db70a8c5..719f63e4 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 9276bed5..6d348787 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 6c1814dc..b05df740 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 864302f4..2269b78e 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 db4b71e5..579897cc 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 145f1316..27a5065b 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 964aa307..259b8fa8 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 73f393e7..d6233b09 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 058ddb9d..af089963 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 248/291] 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 4937d2b7..035c2dea 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 44af4cc1..55a21627 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 249/291] 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 33389b28..4a35866d 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 9ea0895c..c6db033e 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 51366409..b76011f2 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 250/291] 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 4a35866d..a84536f7 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 c6db033e..8cf74651 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 251/291] 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 b76011f2..875dd619 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 252/291] 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 baa4b0f9..1f356515 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 66190b30..211fbb1b 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 253/291] 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 a84536f7..f5f6f3ae 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 8cf74651..810315bb 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 875dd619..8cb53cc2 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 254/291] 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 211fbb1b..dda59978 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 255/291] docs(readme): add Portuguese documentation. (#4078) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index faeb4952..0464107c 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 256/291] 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 8cb53cc2..a463e820 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 257/291] 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 1f356515..77232f90 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 dda59978..62a1e14f 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 258/291] 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 48cc15c9..e17596aa 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 259/291] 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 947abf9c..74983c50 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 b0a5f982..0d3e5a8c 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 3aa3a594..74eb6104 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 260/291] 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 77232f90..1e8c8e27 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 261/291] 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 1e8c8e27..cab14529 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 62a1e14f..6921deab 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 262/291] 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 8d58c989..c3ae7275 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 810315bb..1277fd5f 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 6921deab..7c414843 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 719f63e4..5d0c47d3 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 6d348787..0c86af2e 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 fa8ab894..ee063cd1 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 d6233b09..49f355a7 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 263/291] 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 c3ae7275..ccb26684 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 f7175929..9166e3b0 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 5b7929b8..3a8d53f3 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 a8116391..7a5db34b 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 2036b59b..901e9740 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 9782b81d..c93f2141 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 7c414843..91d5e898 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 edf4bb12..0efbfd78 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 0240b2ec..6c8f2a7f 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 5d0c47d3..732da18b 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 b05df740..de00c499 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 acdf89c4..eafc60ad 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 ee063cd1..08eec1e4 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 27a5065b..ad633b00 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 49f355a7..995ff51c 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 af089963..8098c681 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 264/291] 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 cab14529..c724daf3 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 91d5e898..5b63a647 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 265/291] 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 035c2dea..5de7d065 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 55a21627..d0389bb7 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 266/291] 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 8ae11823..22edf453 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 5b63a647..ef0cfccd 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 267/291] 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 74983c50..1d193efb 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 0464107c..ae155048 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 62085c5d..43dcd722 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 0efbfd78..4b440e3a 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 5de7d065..398787ea 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 d0389bb7..ddd0f87d 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= From c3c8620a7fb4e09c7845feca4e8e8a8678a2685d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 08:50:51 +0800 Subject: [PATCH 268/291] chore(deps): bump github.com/go-playground/validator/v10 from 10.20.0 to 10.22.1 (#4052) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.20.0 to 10.22.1. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.20.0...v10.22.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] 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 398787ea..85ad56ff 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22 require ( github.com/bytedance/sonic v1.11.6 github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.20.0 + github.com/go-playground/validator/v10 v10.22.1 github.com/goccy/go-json v0.10.2 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.20 diff --git a/go.sum b/go.sum index ddd0f87d..f4ab17cb 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,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.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/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= From 3b28645dc95d58e0df36b8aff7a6c64f7c0ca5e9 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Wed, 12 Feb 2025 10:22:02 +0800 Subject: [PATCH 269/291] ci: add go version 1.24 to GitHub Actions (#4154) - Add Go version `1.24` to the GitHub Actions workflow 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 1d193efb..095dea6d 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.22", "1.23"] + go: ["1.22", "1.23", "1.24"] test-tags: ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json", "-race"] include: From 1eb827240e520804ea83b1aafcbaf7ba728b81dd Mon Sep 17 00:00:00 2001 From: NezhaFan Date: Tue, 18 Mar 2025 22:12:36 +0800 Subject: [PATCH 270/291] docs: fix case error of X-Real-IP (#4185) Co-authored-by: voyager1 --- CHANGELOG.md | 2 +- context.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de47c750..5648902d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -488,7 +488,7 @@ - [FIX] Refactor render - [FIX] Reworked tests - [FIX] logger now supports cygwin -- [FIX] Use X-Forwarded-For before X-Real-Ip +- [FIX] Use X-Forwarded-For before X-Real-IP - [FIX] time.Time binding (#904) ## Gin 1.1.4 diff --git a/context.go b/context.go index c724daf3..408f1861 100644 --- a/context.go +++ b/context.go @@ -889,7 +889,7 @@ func (c *Context) ShouldBindBodyWithPlain(obj any) error { // 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]). +// 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. func (c *Context) ClientIP() string { From a4baac6e5e030ca707e519a3bf209d25699e3902 Mon Sep 17 00:00:00 2001 From: NezhaFan Date: Tue, 18 Mar 2025 22:14:38 +0800 Subject: [PATCH 271/291] refactor(context):Avoid using filepath.Dir twice in SaveUploadedFile (#4181) Co-authored-by: voyager1 --- context.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/context.go b/context.go index 408f1861..1c76c0f6 100644 --- a/context.go +++ b/context.go @@ -684,15 +684,15 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string, perm } defer src.Close() - if len(perm) <= 0 { - perm = append(perm, 0o750) + var mode os.FileMode = 0o750 + if len(perm) > 0 { + mode = perm[0] } - - if err = os.MkdirAll(filepath.Dir(dst), perm[0]); err != nil { + dir := filepath.Dir(dst) + if err = os.MkdirAll(dir, mode); err != nil { return err } - - if err = os.Chmod(filepath.Dir(dst), perm[0]); err != nil { + if err = os.Chmod(dir, mode); err != nil { return err } From 733ee094fc4aaf016fb05820f553eeb0b81d0f1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 22:15:13 +0800 Subject: [PATCH 272/291] chore(deps): bump golang.org/x/net from 0.33.0 to 0.37.0 (#4178) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.37.0. - [Commits](https://github.com/golang/net/compare/v0.33.0...v0.37.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 | 9 +++++---- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 85ad56ff..a8032b70 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,7 @@ module github.com/gin-gonic/gin go 1.22 +toolchain go1.23.7 require ( github.com/bytedance/sonic v1.11.6 @@ -13,7 +14,7 @@ require ( 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 + golang.org/x/net v0.37.0 google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -38,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.31.0 // indirect + golang.org/x/crypto v0.36.0 // 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 + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect ) diff --git a/go.sum b/go.sum index f4ab17cb..d28753ab 100644 --- a/go.sum +++ b/go.sum @@ -84,22 +84,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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= 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= -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/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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.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/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 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 ebe5e2a6bfdca50fd44074b470ad486392e2933f Mon Sep 17 00:00:00 2001 From: Name <1911860538@qq.com> Date: Tue, 18 Mar 2025 23:13:03 +0800 Subject: [PATCH 273/291] fix(golangci.yml): move fiximports to goimports section and replace exportloopref with copyloopvar (#4167) Co-authored-by: huangzw --- .golangci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index ccb26684..b50a911b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,7 +7,7 @@ linters: - durationcheck - errcheck - errorlint - - exportloopref + - copyloopvar - gci - gofmt - goimports @@ -39,10 +39,11 @@ linters-settings: perfsprint: err-error: true errorf: true - fiximports: true int-conversion: true sprintf1: true strconcat: true + goimports: + fiximports: true testifylint: enable-all: true From 90cf4602698dcbce18df3165b2d24e2940670a41 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 20 Mar 2025 10:13:47 +0800 Subject: [PATCH 274/291] chore: update Go versions and dependencies for improved compatibility (#4187) * chore: update Go versions and dependencies for improved compatibility - Update Go versions in workflow file to `1.23` and `1.24` - Enhance test tags in workflow with specific linker flags - Remove the conditional formatting step for Go `1.22.x` in workflow - Remove `goimports` settings from `.golangci.yml` - Update `go.mod` to use Go `1.23.0` - Upgrade `github.com/bytedance/sonic` from `v1.11.6` to `v1.13.1` - Update indirect dependencies `sonic/loader` to `v0.2.4` and `base64x` to `v0.1.5` in `go.mod` Signed-off-by: appleboy * chore: update project for Go 1.23 compatibility and documentation fixes - Update Go version requirement from 1.22 to 1.23 in README.md - Remove superfluous `$` from example command in README.md - Update warning message to reflect new Go version requirement in debug.go - Update test assertion to reflect new Go version requirement in debug_test.go Signed-off-by: appleboy --------- Signed-off-by: appleboy --- .github/workflows/gin.yml | 14 ++++++++------ .golangci.yml | 2 -- README.md | 4 ++-- debug.go | 2 +- debug_test.go | 2 +- go.mod | 10 ++++------ go.sum | 13 ++++++------- 7 files changed, 22 insertions(+), 25 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 095dea6d..1062252a 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -33,9 +33,15 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: ["1.22", "1.23", "1.24"] + go: ["1.23", "1.24"] test-tags: - ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json", "-race"] + [ + "", + "-tags nomsgpack", + '--ldflags="-checklinkname=0" -tags "sonic avx"', + "-tags go_json", + "-race", + ] include: - os: ubuntu-latest go-build: ~/.cache/go-build @@ -75,7 +81,3 @@ jobs: uses: codecov/codecov-action@v4 with: flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }} - - - name: Format - if: matrix.go-version == '1.22.x' - run: diff -u <(echo -n) <(gofmt -d .) diff --git a/.golangci.yml b/.golangci.yml index b50a911b..925e1306 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -42,8 +42,6 @@ linters-settings: int-conversion: true sprintf1: true strconcat: true - goimports: - fiximports: true testifylint: enable-all: true diff --git a/README.md b/README.md index ae155048..fe5722b2 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.22](https://go.dev/doc/devel/release#go1.22.0) or above. +Gin requires [Go](https://go.dev/) version [1.23](https://go.dev/doc/devel/release#go1.23.0) or above. ### Getting Gin @@ -73,7 +73,7 @@ func main() { To run the code, use the `go run` command, like: ```sh -$ go run example.go +go run example.go ``` Then visit [`0.0.0.0:8080/ping`](http://0.0.0.0:8080/ping) in your browser to see the response! diff --git a/debug.go b/debug.go index 43dcd722..f2016168 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.22+. + debugPrint(`[WARNING] Now Gin requires Go 1.23+. `) } diff --git a/debug_test.go b/debug_test.go index 4b440e3a..59b61beb 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.22+.\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.23+.\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 a8032b70..1223267c 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,9 @@ module github.com/gin-gonic/gin -go 1.22 -toolchain go1.23.7 +go 1.23.0 require ( - github.com/bytedance/sonic v1.11.6 + github.com/bytedance/sonic v1.13.1 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.22.1 github.com/goccy/go-json v0.10.2 @@ -20,9 +19,8 @@ require ( ) require ( - 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/bytedance/sonic/loader v0.2.4 // indirect + github.com/cloudwego/base64x v0.1.5 // 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 d28753ab..2e1c30ad 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,13 @@ -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 v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g= +github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= 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/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 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= @@ -112,4 +112,3 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C 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 4ccfa7c275c449990818e46759d5974a953cc1c1 Mon Sep 17 00:00:00 2001 From: takanuva15 <6986426+takanuva15@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:33:10 -0400 Subject: [PATCH 275/291] feat(binding): add support for unixMilli and unixMicro (#4190) --- binding/binding_test.go | 26 ++++++++++++++++---------- binding/form_mapping.go | 16 +++++++++++----- docs/doc.md | 6 +++++- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/binding/binding_test.go b/binding/binding_test.go index 901e9740..bdab3694 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -69,15 +69,19 @@ type FooStructDisallowUnknownFields struct { } type FooBarStructForTimeType struct { - TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_utc:"1" time_location:"Asia/Chongqing"` - TimeBar time.Time `form:"time_bar" time_format:"2006-01-02" time_utc:"1"` - CreateTime time.Time `form:"createTime" time_format:"unixNano"` - UnixTime time.Time `form:"unixTime" time_format:"unix"` + TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_utc:"1" time_location:"Asia/Chongqing"` + TimeBar time.Time `form:"time_bar" time_format:"2006-01-02" time_utc:"1"` + CreateTime time.Time `form:"createTime" time_format:"unixNano"` + UnixTime time.Time `form:"unixTime" time_format:"unix"` + UnixMilliTime time.Time `form:"unixMilliTime" time_format:"unixmilli"` + UnixMicroTime time.Time `form:"unixMicroTime" time_format:"uNiXmiCrO"` } type FooStructForTimeTypeNotUnixFormat struct { - CreateTime time.Time `form:"createTime" time_format:"unixNano"` - UnixTime time.Time `form:"unixTime" time_format:"unix"` + CreateTime time.Time `form:"createTime" time_format:"unixNano"` + UnixTime time.Time `form:"unixTime" time_format:"unix"` + UnixMilliTime time.Time `form:"unixMilliTime" time_format:"unixMilli"` + UnixMicroTime time.Time `form:"unixMicroTime" time_format:"unixMicro"` } type FooStructForTimeTypeNotFormat struct { @@ -265,10 +269,10 @@ func TestBindingFormDefaultValue2(t *testing.T) { func TestBindingFormForTime(t *testing.T) { testFormBindingForTime(t, http.MethodPost, "/", "/", - "time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "bar2=foo") + "time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033&unixMilliTime=1562400033001&unixMicroTime=1562400033000012", "bar2=foo") testFormBindingForTimeNotUnixFormat(t, http.MethodPost, "/", "/", - "time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo") + "time_foo=2017-11-15&createTime=bad&unixTime=bad&unixMilliTime=bad&unixMicroTime=bad", "bar2=foo") testFormBindingForTimeNotFormat(t, http.MethodPost, "/", "/", "time_foo=2017-11-15", "bar2=foo") @@ -282,11 +286,11 @@ func TestBindingFormForTime(t *testing.T) { func TestBindingFormForTime2(t *testing.T) { testFormBindingForTime(t, http.MethodGet, - "/?time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "/?bar2=foo", + "/?time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033&unixMilliTime=1562400033001&unixMicroTime=1562400033000012", "/?bar2=foo", "", "") testFormBindingForTimeNotUnixFormat(t, http.MethodPost, "/", "/", - "time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo") + "time_foo=2017-11-15&createTime=bad&unixTime=bad&unixMilliTime=bad&unixMicroTime=bad", "bar2=foo") testFormBindingForTimeNotFormat(t, http.MethodGet, "/?time_foo=2017-11-15", "/?bar2=foo", "", "") @@ -952,6 +956,8 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s assert.Equal(t, "UTC", obj.TimeBar.Location().String()) assert.Equal(t, int64(1562400033000000123), obj.CreateTime.UnixNano()) assert.Equal(t, int64(1562400033), obj.UnixTime.Unix()) + assert.Equal(t, int64(1562400033001), obj.UnixMilliTime.UnixMilli()) + assert.Equal(t, int64(1562400033000012), obj.UnixMicroTime.UnixMicro()) obj = FooBarStructForTimeType{} req = requestWithBody(method, badPath, badBody) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index f5f6f3ae..235692d2 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -398,18 +398,24 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val } switch tf := strings.ToLower(timeFormat); tf { - case "unix", "unixnano": + case "unix", "unixmilli", "unixmicro", "unixnano": tv, err := strconv.ParseInt(val, 10, 64) if err != nil { return err } - d := time.Duration(1) - if tf == "unixnano" { - d = time.Second + var t time.Time + switch tf { + case "unix": + t = time.Unix(tv, 0) + case "unixmilli": + t = time.UnixMilli(tv) + case "unixmicro": + t = time.UnixMicro(tv) + default: + t = time.Unix(0, tv) } - t := time.Unix(tv/int64(d), tv%int64(d)) value.Set(reflect.ValueOf(t)) return nil } diff --git a/docs/doc.md b/docs/doc.md index a463e820..bea417b2 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -832,6 +832,8 @@ type Person struct { 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"` + UnixMilliTime time.Time `form:"unixMilliTime" time_format:"unixmilli"` + UnixMicroTime time.Time `form:"unixMicroTime" time_format:"uNiXmIcRo"` // case does not matter for "unix*" time formats } func main() { @@ -851,6 +853,8 @@ func startPage(c *gin.Context) { log.Println(person.Birthday) log.Println(person.CreateTime) log.Println(person.UnixTime) + log.Println(person.UnixMilliTime) + log.Println(person.UnixMicroTime) } c.String(http.StatusOK, "Success") @@ -860,7 +864,7 @@ func startPage(c *gin.Context) { 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&unixMilliTime=1562400033001&unixMicroTime=1562400033000012" ``` From e737e3e267beb4dc3bab16cc8be59e3902d98a94 Mon Sep 17 00:00:00 2001 From: revevide <158151416+revevide@users.noreply.github.com> Date: Thu, 20 Mar 2025 23:35:49 +0800 Subject: [PATCH 276/291] fix(binding): prevent duplicate decoding and add validation in decodeToml (#4193) --- binding/toml.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/toml.go b/binding/toml.go index a66b93aa..2681231d 100644 --- a/binding/toml.go +++ b/binding/toml.go @@ -31,5 +31,5 @@ func decodeToml(r io.Reader, obj any) error { if err := decoder.Decode(obj); err != nil { return err } - return decoder.Decode(obj) + return validate(obj) } From 8763f33c65f7df8be5b9fe7504ab7fcf20abb41d Mon Sep 17 00:00:00 2001 From: bound2 <9380102+bound2@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:40:41 +0200 Subject: [PATCH 277/291] fix: prevent middleware re-entry issue in HandleContext (#3987) --- gin.go | 2 ++ gin_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/gin.go b/gin.go index e17596aa..0761c14d 100644 --- a/gin.go +++ b/gin.go @@ -637,10 +637,12 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Disclaimer: You can loop yourself to deal with this, use wisely. func (engine *Engine) HandleContext(c *Context) { oldIndexValue := c.index + oldHandlers := c.handlers c.reset() engine.handleHTTPRequest(c) c.index = oldIndexValue + c.handlers = oldHandlers } func (engine *Engine) handleHTTPRequest(c *Context) { diff --git a/gin_test.go b/gin_test.go index 732da18b..850ae09b 100644 --- a/gin_test.go +++ b/gin_test.go @@ -573,6 +573,44 @@ func TestEngineHandleContextManyReEntries(t *testing.T) { assert.Equal(t, int64(expectValue), middlewareCounter) } +func TestEngineHandleContextPreventsMiddlewareReEntry(t *testing.T) { + // given + var handlerCounterV1, handlerCounterV2, middlewareCounterV1 int64 + + r := New() + v1 := r.Group("/v1") + { + v1.Use(func(c *Context) { + atomic.AddInt64(&middlewareCounterV1, 1) + }) + v1.GET("/test", func(c *Context) { + atomic.AddInt64(&handlerCounterV1, 1) + c.Status(http.StatusOK) + }) + } + + v2 := r.Group("/v2") + { + v2.GET("/test", func(c *Context) { + c.Request.URL.Path = "/v1/test" + r.HandleContext(c) + }, func(c *Context) { + atomic.AddInt64(&handlerCounterV2, 1) + }) + } + + // when + responseV1 := PerformRequest(r, "GET", "/v1/test") + responseV2 := PerformRequest(r, "GET", "/v2/test") + + // then + assert.Equal(t, 200, responseV1.Code) + assert.Equal(t, 200, responseV2.Code) + assert.Equal(t, int64(2), handlerCounterV1) + assert.Equal(t, int64(2), middlewareCounterV1) + assert.Equal(t, int64(1), handlerCounterV2) +} + func TestPrepareTrustedCIRDsWith(t *testing.T) { r := New() From 3afff295a2e638075fcfbd2c38ee89a4263637d3 Mon Sep 17 00:00:00 2001 From: Andrey Bolonin Date: Fri, 11 Apr 2025 18:58:02 +0300 Subject: [PATCH 278/291] docs: add Upd language list (#4211) * Upd language list * Update url --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fe5722b2..aa8ada4e 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ The documentation is also available on [gin-gonic.com](https://gin-gonic.com) in - [Turkish](https://gin-gonic.com/tr/docs/) - [Persian](https://gin-gonic.com/fa/docs/) - [Português](https://gin-gonic.com/pt/docs/) +- [Russian](https://gin-gonic.com/ru/docs/) ### Articles From 1b53a477904c783c82cd6cc402aece4b9558a66e Mon Sep 17 00:00:00 2001 From: Adlai Bridson-Boyczuk <71486949+boyczuk@users.noreply.github.com> Date: Fri, 11 Apr 2025 11:59:03 -0400 Subject: [PATCH 279/291] docs: Fixing English grammar mistakes and awkward sentence structure in doc/doc.md (#4207) * docs: Fixing grammar mistakes and awkward sentences, such as modeling binding section * Update doc.md Missed grammar mistake --- docs/doc.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/doc.md b/docs/doc.md index bea417b2..cd651390 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -70,7 +70,7 @@ ### Build with json replacement -Gin uses `encoding/json` as default json package but you can change it by build from other tags. +Gin uses `encoding/json` as the default JSON package but you can change it by building from other tags. [jsoniter](https://github.com/json-iterator/go) @@ -84,7 +84,7 @@ 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.) +[sonic](https://github.com/bytedance/sonic) (you have to ensure that your cpu supports avx instruction.) ```sh $ go build -tags="sonic avx" . @@ -120,7 +120,7 @@ func main() { router.HEAD("/someHead", head) router.OPTIONS("/someOptions", options) - // By default it serves on :8080 unless a + // By default, it serves on :8080 unless a // PORT environment variable was defined. router.Run() // router.Run(":3000") for a hard coded port @@ -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 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") @@ -300,7 +300,7 @@ curl -X POST http://localhost:8080/upload \ #### Multiple files -See the detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/multiple). +See the detailed [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/multiple). ```go func main() { @@ -704,7 +704,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-validation: Running the example above using the `curl` command returns an error. This is because the example uses `binding:"required"` for `Password`. If instead, you use `binding:"-"` for `Password`, then it will not return an error when you run the example again. ### Custom Validators @@ -1187,7 +1187,7 @@ func main() { }) r.GET("/moreJSON", func(c *gin.Context) { - // You also can use a struct + // You can also use a struct var msg struct { Name string `json:"user"` Message string @@ -1490,7 +1490,7 @@ You may use custom delims #### Custom Template Funcs -See the detail [example code](https://github.com/gin-gonic/examples/tree/master/template). +See the detailed [example code](https://github.com/gin-gonic/examples/tree/master/template). main.go @@ -1542,7 +1542,7 @@ 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`. +Gin allows only one html.Template by default. Check [a multitemplate render](https://github.com/gin-contrib/multitemplate) for using features like go 1.6 `block template`. ### Redirects @@ -2091,7 +2091,7 @@ type formB struct { func SomeHandler(c *gin.Context) { objA := formA{} objB := formB{} - // This c.ShouldBind consumes c.Request.Body and it cannot be reused. + // Calling 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. @@ -2324,7 +2324,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 trusts 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 From 49e9137c68e6dfaa529a2d0c9fe64d9e69a8554e Mon Sep 17 00:00:00 2001 From: NezhaFan Date: Sat, 12 Apr 2025 00:00:59 +0800 Subject: [PATCH 280/291] docs: fix comment (#4205) Co-authored-by: voyager1 --- binding/protobuf.go | 2 +- ginS/gins.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/binding/protobuf.go b/binding/protobuf.go index 57721fc9..259ae8e7 100644 --- a/binding/protobuf.go +++ b/binding/protobuf.go @@ -34,7 +34,7 @@ func (protobufBinding) BindBody(body []byte, obj any) error { 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 + // Here it's same to return validate(obj), but until now we can't add // `binding:""` to the struct which automatically generate by gen-proto return nil // return validate(obj) diff --git a/ginS/gins.go b/ginS/gins.go index ea38c613..1dcb1919 100644 --- a/ginS/gins.go +++ b/ginS/gins.go @@ -154,7 +154,7 @@ func RunUnix(file string) (err error) { // RunFd attaches the router to a http.Server and starts listening and serving HTTP requests // through the specified file descriptor. -// Note: the method will block the calling goroutine indefinitely unless on error happens. +// Note: the method will block the calling goroutine indefinitely unless an error happens. func RunFd(fd int) (err error) { return engine().RunFd(fd) } From 3319038418656a268c889393cb2dd4224c4469ec Mon Sep 17 00:00:00 2001 From: eduardo-ax Date: Sun, 20 Apr 2025 13:01:03 -0300 Subject: [PATCH 281/291] fix(readme): fix broken link to English documentation (#4222) Co-authored-by: Eduardo Alexandre --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa8ada4e..9f548cc0 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ See the [API documentation on go.dev](https://pkg.go.dev/github.com/gin-gonic/gi The documentation is also available on [gin-gonic.com](https://gin-gonic.com) in several languages: -- [English](https://gin-gonic.com/docs/) +- [English](https://gin-gonic.com/en/docs/) - [简体中文](https://gin-gonic.com/zh-cn/docs/) - [繁體中文](https://gin-gonic.com/zh-tw/docs/) - [日本語](https://gin-gonic.com/ja/docs/) From 56fccc39ec5cbe30e39fa34e67371219354f14cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 00:01:35 +0800 Subject: [PATCH 282/291] chore(deps): bump golang.org/x/net from 0.37.0 to 0.38.0 (#4221) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.37.0 to 0.38.0. - [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.38.0 dependency-type: direct:production ... 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 1223267c..1756a917 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( 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.37.0 + golang.org/x/net v0.38.0 google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 2e1c30ad..71fc142a 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +90,8 @@ golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJ 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.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From afa0c31d97e1b112ccfe7652957f7d8514580c72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 00:02:02 +0800 Subject: [PATCH 283/291] chore(deps): bump github.com/gin-contrib/sse from 0.1.0 to 1.1.0 (#4216) Bumps [github.com/gin-contrib/sse](https://github.com/gin-contrib/sse) from 0.1.0 to 1.1.0. - [Release notes](https://github.com/gin-contrib/sse/releases) - [Changelog](https://github.com/gin-contrib/sse/blob/master/.goreleaser.yaml) - [Commits](https://github.com/gin-contrib/sse/compare/v0.1.0...v1.1.0) --- updated-dependencies: - dependency-name: github.com/gin-contrib/sse dependency-version: 1.1.0 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> --- go.mod | 4 ++-- go.sum | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 1756a917..b993ca53 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,14 @@ go 1.23.0 require ( github.com/bytedance/sonic v1.13.1 - github.com/gin-contrib/sse v0.1.0 + github.com/gin-contrib/sse v1.1.0 github.com/go-playground/validator/v10 v10.22.1 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.2 github.com/quic-go/quic-go v0.48.2 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/ugorji/go/codec v1.2.12 golang.org/x/net v0.38.0 google.golang.org/protobuf v1.34.1 diff --git a/go.sum b/go.sum index 71fc142a..c107dc03 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ 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.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/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= 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= @@ -74,8 +74,9 @@ 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.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/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.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= From 0eb99493c28b09cee339061b0d8a11c9a4f31399 Mon Sep 17 00:00:00 2001 From: Name <1911860538@qq.com> Date: Mon, 21 Apr 2025 00:05:34 +0800 Subject: [PATCH 284/291] perf: optimize AsciiJSON.Render method by using fmt.Appendf and reusing temp buffer (#4175) per: use bytesconv.BytesToString(ret) instead of string(str) Co-authored-by: 1911860538 --- render/json.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/render/json.go b/render/json.go index fc8dea45..a6b54dc3 100644 --- a/render/json.go +++ b/render/json.go @@ -151,7 +151,7 @@ func (r JsonpJSON) WriteContentType(w http.ResponseWriter) { } // Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType. -func (r AsciiJSON) Render(w http.ResponseWriter) (err error) { +func (r AsciiJSON) Render(w http.ResponseWriter) error { r.WriteContentType(w) ret, err := json.Marshal(r.Data) if err != nil { @@ -159,12 +159,15 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) { } var buffer bytes.Buffer + escapeBuf := make([]byte, 0, 6) // Preallocate 6 bytes for Unicode escape sequences + for _, r := range bytesconv.BytesToString(ret) { - cvt := string(r) if r >= 128 { - cvt = fmt.Sprintf("\\u%04x", int64(r)) + escapeBuf = fmt.Appendf(escapeBuf[:0], "\\u%04x", r) // Reuse escapeBuf + buffer.Write(escapeBuf) + } else { + buffer.WriteByte(byte(r)) } - buffer.WriteString(cvt) } _, err = w.Write(buffer.Bytes()) From 71496abe6836462e2ed70436b7d72ea2a3585417 Mon Sep 17 00:00:00 2001 From: sunshineplan Date: Mon, 21 Apr 2025 00:11:16 +0800 Subject: [PATCH 285/291] feat(fs): Implement loading HTML from http.FileSystem (#4053) * Implement loading HTML from http.FileSystem * Add OnlyHTMLFS test * Move OnlyHTMLFS to internal and add test --- docs/doc.md | 8 ++- gin.go | 14 ++++++ ginS/gins.go | 5 ++ gin_test.go | 109 +++++++++++++++++++++++++++++++++++++++++ internal/fs/fs.go | 22 +++++++++ internal/fs/fs_test.go | 49 ++++++++++++++++++ render/html.go | 18 +++++-- render/render_test.go | 51 ++++++++++++++----- 8 files changed, 258 insertions(+), 18 deletions(-) create mode 100644 internal/fs/fs.go create mode 100644 internal/fs/fs_test.go diff --git a/docs/doc.md b/docs/doc.md index cd651390..ce466652 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -1393,13 +1393,19 @@ func main() { ### HTML rendering -Using LoadHTMLGlob() or LoadHTMLFiles() +Using LoadHTMLGlob() or LoadHTMLFiles() or LoadHTMLFS() ```go +//go:embed templates/* +var templates embed.FS + func main() { router := gin.Default() router.LoadHTMLGlob("templates/*") //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") + //router.LoadHTMLFS(http.Dir("templates"), "template1.html", "template2.html") + //or + //router.LoadHTMLFS(http.FS(templates), "templates/template1.html", "templates/template2.html") router.GET("/index", func(c *gin.Context) { c.HTML(http.StatusOK, "index.tmpl", gin.H{ "title": "Main website", diff --git a/gin.go b/gin.go index 0761c14d..f9813e1d 100644 --- a/gin.go +++ b/gin.go @@ -16,6 +16,7 @@ import ( "sync" "github.com/gin-gonic/gin/internal/bytesconv" + filesystem "github.com/gin-gonic/gin/internal/fs" "github.com/gin-gonic/gin/render" "github.com/quic-go/quic-go/http3" @@ -285,6 +286,19 @@ func (engine *Engine) LoadHTMLFiles(files ...string) { engine.SetHTMLTemplate(templ) } +// LoadHTMLFS loads an http.FileSystem and a slice of patterns +// and associates the result with HTML renderer. +func (engine *Engine) LoadHTMLFS(fs http.FileSystem, patterns ...string) { + if IsDebugging() { + engine.HTMLRender = render.HTMLDebug{FileSystem: fs, Patterns: patterns, FuncMap: engine.FuncMap, Delims: engine.delims} + return + } + + templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFS( + filesystem.FileSystem{FileSystem: fs}, patterns...)) + engine.SetHTMLTemplate(templ) +} + // SetHTMLTemplate associate a template with HTML renderer. func (engine *Engine) SetHTMLTemplate(templ *template.Template) { if len(engine.trees) > 0 { diff --git a/ginS/gins.go b/ginS/gins.go index 1dcb1919..3e6a92eb 100644 --- a/ginS/gins.go +++ b/ginS/gins.go @@ -32,6 +32,11 @@ func LoadHTMLFiles(files ...string) { engine().LoadHTMLFiles(files...) } +// LoadHTMLFS is a wrapper for Engine.LoadHTMLFS. +func LoadHTMLFS(fs http.FileSystem, patterns ...string) { + engine().LoadHTMLFS(fs, patterns...) +} + // SetHTMLTemplate is a wrapper for Engine.SetHTMLTemplate. func SetHTMLTemplate(templ *template.Template) { engine().SetHTMLTemplate(templ) diff --git a/gin_test.go b/gin_test.go index 850ae09b..a80b690e 100644 --- a/gin_test.go +++ b/gin_test.go @@ -325,6 +325,115 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) { assert.Equal(t, "Date: 2017/07/01", string(resp)) } +var tmplFS = http.Dir("testdata/template") + +func TestLoadHTMLFSTestMode(t *testing.T) { + ts := setupHTMLFiles( + t, + TestMode, + false, + func(router *Engine) { + router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) + if err != nil { + t.Error(err) + } + + resp, _ := io.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp)) +} + +func TestLoadHTMLFSDebugMode(t *testing.T) { + ts := setupHTMLFiles( + t, + DebugMode, + false, + func(router *Engine) { + router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) + if err != nil { + t.Error(err) + } + + resp, _ := io.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp)) +} + +func TestLoadHTMLFSReleaseMode(t *testing.T) { + ts := setupHTMLFiles( + t, + ReleaseMode, + false, + func(router *Engine) { + router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) + if err != nil { + t.Error(err) + } + + resp, _ := io.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp)) +} + +func TestLoadHTMLFSUsingTLS(t *testing.T) { + ts := setupHTMLFiles( + t, + TestMode, + true, + func(router *Engine) { + router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl") + }, + ) + defer ts.Close() + + // Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + client := &http.Client{Transport: tr} + res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) + if err != nil { + t.Error(err) + } + + resp, _ := io.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp)) +} + +func TestLoadHTMLFSFuncMap(t *testing.T) { + ts := setupHTMLFiles( + t, + TestMode, + false, + func(router *Engine) { + router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) + if err != nil { + t.Error(err) + } + + resp, _ := io.ReadAll(res.Body) + assert.Equal(t, "Date: 2017/07/01", string(resp)) +} + func TestAddRoute(t *testing.T) { router := New() router.addRoute(http.MethodGet, "/", HandlersChain{func(_ *Context) {}}) diff --git a/internal/fs/fs.go b/internal/fs/fs.go new file mode 100644 index 00000000..524ac08b --- /dev/null +++ b/internal/fs/fs.go @@ -0,0 +1,22 @@ +package fs + +import ( + "io/fs" + "net/http" +) + +// FileSystem implements an [fs.FS]. +type FileSystem struct { + http.FileSystem +} + +// Open passes `Open` to the upstream implementation and return an [fs.File]. +func (o FileSystem) Open(name string) (fs.File, error) { + f, err := o.FileSystem.Open(name) + + if err != nil { + return nil, err + } + + return fs.File(f), nil +} diff --git a/internal/fs/fs_test.go b/internal/fs/fs_test.go new file mode 100644 index 00000000..113e92b6 --- /dev/null +++ b/internal/fs/fs_test.go @@ -0,0 +1,49 @@ +package fs + +import ( + "errors" + "net/http" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mockFileSystem struct { + open func(name string) (http.File, error) +} + +func (m *mockFileSystem) Open(name string) (http.File, error) { + return m.open(name) +} + +func TesFileSystem_Open(t *testing.T) { + var testFile *os.File + mockFS := &mockFileSystem{ + open: func(name string) (http.File, error) { + return testFile, nil + }, + } + fs := &FileSystem{mockFS} + + file, err := fs.Open("foo") + + require.NoError(t, err) + assert.Equal(t, testFile, file) +} + +func TestFileSystem_Open_err(t *testing.T) { + testError := errors.New("mock") + mockFS := &mockFileSystem{ + open: func(_ string) (http.File, error) { + return nil, testError + }, + } + fs := &FileSystem{mockFS} + + file, err := fs.Open("foo") + + require.ErrorIs(t, err, testError) + assert.Nil(t, file) +} diff --git a/render/html.go b/render/html.go index c308408d..f5e7455a 100644 --- a/render/html.go +++ b/render/html.go @@ -7,6 +7,8 @@ package render import ( "html/template" "net/http" + + "github.com/gin-gonic/gin/internal/fs" ) // Delims represents a set of Left and Right delimiters for HTML template rendering. @@ -31,10 +33,12 @@ type HTMLProduction struct { // HTMLDebug contains template delims and pattern and function with file list. type HTMLDebug struct { - Files []string - Glob string - Delims Delims - FuncMap template.FuncMap + Files []string + Glob string + FileSystem http.FileSystem + Patterns []string + Delims Delims + FuncMap template.FuncMap } // HTML contains template reference and its name with given interface object. @@ -73,7 +77,11 @@ func (r HTMLDebug) loadTemplate() *template.Template { if r.Glob != "" { return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob)) } - panic("the HTML debug render was created without files or glob pattern") + if r.FileSystem != nil && len(r.Patterns) > 0 { + return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFS( + fs.FileSystem{FileSystem: r.FileSystem}, r.Patterns...)) + } + panic("the HTML debug render was created without files or glob pattern or file system with patterns") } // Render (HTML) executes template and writes its result with custom ContentType for response. diff --git a/render/render_test.go b/render/render_test.go index ad633b00..4dd2a3af 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -489,10 +489,12 @@ func TestRenderHTMLTemplateEmptyName(t *testing.T) { func TestRenderHTMLDebugFiles(t *testing.T) { w := httptest.NewRecorder() htmlRender := HTMLDebug{ - Files: []string{"../testdata/template/hello.tmpl"}, - Glob: "", - Delims: Delims{Left: "{[{", Right: "}]}"}, - FuncMap: nil, + Files: []string{"../testdata/template/hello.tmpl"}, + Glob: "", + FileSystem: nil, + Patterns: nil, + Delims: Delims{Left: "{[{", Right: "}]}"}, + FuncMap: nil, } instance := htmlRender.Instance("hello.tmpl", map[string]any{ "name": "thinkerou", @@ -508,10 +510,33 @@ func TestRenderHTMLDebugFiles(t *testing.T) { func TestRenderHTMLDebugGlob(t *testing.T) { w := httptest.NewRecorder() htmlRender := HTMLDebug{ - Files: nil, - Glob: "../testdata/template/hello*", - Delims: Delims{Left: "{[{", Right: "}]}"}, - FuncMap: nil, + Files: nil, + Glob: "../testdata/template/hello*", + FileSystem: nil, + Patterns: nil, + Delims: Delims{Left: "{[{", Right: "}]}"}, + FuncMap: nil, + } + instance := htmlRender.Instance("hello.tmpl", map[string]any{ + "name": "thinkerou", + }) + + err := instance.Render(w) + + 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")) +} + +func TestRenderHTMLDebugFS(t *testing.T) { + w := httptest.NewRecorder() + htmlRender := HTMLDebug{ + Files: nil, + Glob: "", + FileSystem: http.Dir("../testdata/template"), + Patterns: []string{"hello.tmpl"}, + Delims: Delims{Left: "{[{", Right: "}]}"}, + FuncMap: nil, } instance := htmlRender.Instance("hello.tmpl", map[string]any{ "name": "thinkerou", @@ -526,10 +551,12 @@ func TestRenderHTMLDebugGlob(t *testing.T) { func TestRenderHTMLDebugPanics(t *testing.T) { htmlRender := HTMLDebug{ - Files: nil, - Glob: "", - Delims: Delims{"{{", "}}"}, - FuncMap: nil, + Files: nil, + Glob: "", + FileSystem: nil, + Patterns: nil, + Delims: Delims{"{{", "}}"}, + FuncMap: nil, } assert.Panics(t, func() { htmlRender.Instance("", nil) }) } From 255af882db4baf0ed6209f1a5471f1663c5d0060 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 00:14:45 +0800 Subject: [PATCH 286/291] chore(deps): bump github.com/go-playground/validator/v10 (#4208) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.22.1 to 10.26.0. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.22.1...v10.26.0) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 dependency-version: 10.26.0 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 b993ca53..397db4a4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.0 require ( github.com/bytedance/sonic v1.13.1 github.com/gin-contrib/sse v1.1.0 - github.com/go-playground/validator/v10 v10.22.1 + github.com/go-playground/validator/v10 v10.26.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 @@ -22,7 +22,7 @@ require ( github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cloudwego/base64x v0.1.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect diff --git a/go.sum b/go.sum index c107dc03..d2fc985e 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ 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.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= @@ -24,8 +24,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.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= -github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= 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= From bb824731032856460aa3ffc23bd90e11bf7d5199 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 00:15:51 +0800 Subject: [PATCH 287/291] chore(deps): bump github.com/quic-go/quic-go from 0.48.2 to 0.50.1 (#4197) Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.48.2 to 0.50.1. - [Release notes](https://github.com/quic-go/quic-go/releases) - [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md) - [Commits](https://github.com/quic-go/quic-go/compare/v0.48.2...v0.50.1) --- updated-dependencies: - dependency-name: github.com/quic-go/quic-go 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 | 10 +++++----- go.sum | 20 ++++++++------------ 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 397db4a4..3a7e1ba6 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.20 github.com/pelletier/go-toml/v2 v2.2.2 - github.com/quic-go/quic-go v0.48.2 + github.com/quic-go/quic-go v0.51.0 github.com/stretchr/testify v1.10.0 github.com/ugorji/go/codec v1.2.12 golang.org/x/net v0.38.0 @@ -35,12 +35,12 @@ require ( github.com/pmezard/go-difflib v1.0.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 + go.uber.org/mock v0.5.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/crypto v0.36.0 // indirect - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.22.0 // indirect ) diff --git a/go.sum b/go.sum index d2fc985e..5a7f2adf 100644 --- a/go.sum +++ b/go.sum @@ -61,8 +61,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc= +github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= 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= @@ -81,16 +81,14 @@ 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= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= 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.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -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/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= @@ -101,10 +99,8 @@ golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -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= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 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= From 67c9d4ee110e9adfe33063ef847dba56717c148a Mon Sep 17 00:00:00 2001 From: Name <1911860538@qq.com> Date: Mon, 21 Apr 2025 22:05:28 +0800 Subject: [PATCH 288/291] refactor: replace magic number 128 with unicode.MaxASCII in AsciiJSON Render (#4224) Co-authored-by: huangzw --- render/json.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/render/json.go b/render/json.go index a6b54dc3..23923c44 100644 --- a/render/json.go +++ b/render/json.go @@ -9,6 +9,7 @@ import ( "fmt" "html/template" "net/http" + "unicode" "github.com/gin-gonic/gin/internal/bytesconv" "github.com/gin-gonic/gin/internal/json" @@ -162,7 +163,7 @@ func (r AsciiJSON) Render(w http.ResponseWriter) error { escapeBuf := make([]byte, 0, 6) // Preallocate 6 bytes for Unicode escape sequences for _, r := range bytesconv.BytesToString(ret) { - if r >= 128 { + if r > unicode.MaxASCII { escapeBuf = fmt.Appendf(escapeBuf[:0], "\\u%04x", r) // Reuse escapeBuf buffer.Write(escapeBuf) } else { From 7a1b655074c313f9491c83bb8ea164cdc4a9afe9 Mon Sep 17 00:00:00 2001 From: Yash Date: Sun, 11 May 2025 20:04:09 +0530 Subject: [PATCH 289/291] fix: sonic on arm64 (#4234) --- .github/workflows/gin.yml | 2 +- docs/doc.md | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- internal/json/json.go | 2 +- internal/json/sonic.go | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml index 1062252a..54185a0a 100644 --- a/.github/workflows/gin.yml +++ b/.github/workflows/gin.yml @@ -38,7 +38,7 @@ jobs: [ "", "-tags nomsgpack", - '--ldflags="-checklinkname=0" -tags "sonic avx"', + '--ldflags="-checklinkname=0" -tags sonic', "-tags go_json", "-race", ] diff --git a/docs/doc.md b/docs/doc.md index ce466652..9b7b1ec9 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -84,10 +84,10 @@ go build -tags=jsoniter . go build -tags=go_json . ``` -[sonic](https://github.com/bytedance/sonic) (you have to ensure that your cpu supports avx instruction.) +[sonic](https://github.com/bytedance/sonic) ```sh -$ go build -tags="sonic avx" . +$ go build -tags=sonic . ``` ### Build without `MsgPack` rendering feature diff --git a/go.mod b/go.mod index 3a7e1ba6..15475588 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gin-gonic/gin go 1.23.0 require ( - github.com/bytedance/sonic v1.13.1 + github.com/bytedance/sonic v1.13.2 github.com/gin-contrib/sse v1.1.0 github.com/go-playground/validator/v10 v10.26.0 github.com/goccy/go-json v0.10.2 diff --git a/go.sum b/go.sum index 5a7f2adf..58aea722 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g= -github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= diff --git a/internal/json/json.go b/internal/json/json.go index c7ee83eb..26817786 100644 --- a/internal/json/json.go +++ b/internal/json/json.go @@ -2,7 +2,7 @@ // 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 && !(sonic && avx && (linux || windows || darwin) && amd64) +//go:build !jsoniter && !go_json && !(sonic && (linux || windows || darwin)) package json diff --git a/internal/json/sonic.go b/internal/json/sonic.go index 529e16d0..8cd88049 100644 --- a/internal/json/sonic.go +++ b/internal/json/sonic.go @@ -2,7 +2,7 @@ // 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 +//go:build sonic && (linux || windows || darwin) package json From 4714c2a9a39f0877ccb38089894263f052025a6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 22:34:39 +0800 Subject: [PATCH 290/291] chore(deps): bump google.golang.org/protobuf from 1.34.1 to 1.36.6 (#4198) Bumps google.golang.org/protobuf from 1.34.1 to 1.36.6. --- updated-dependencies: - dependency-name: google.golang.org/protobuf 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 15475588..5aa6b36e 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/ugorji/go/codec v1.2.12 golang.org/x/net v0.38.0 - google.golang.org/protobuf v1.34.1 + google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 58aea722..75b73d8c 100644 --- a/go.sum +++ b/go.sum @@ -101,8 +101,8 @@ golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 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 cf32d2dcf8c7534f59727c5e213e45f2412c593a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 22:35:03 +0800 Subject: [PATCH 291/291] chore(deps): bump github.com/pelletier/go-toml/v2 from 2.2.2 to 2.2.4 (#4212) Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.2.2 to 2.2.4. - [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.2.2...v2.2.4) --- updated-dependencies: - dependency-name: github.com/pelletier/go-toml/v2 dependency-version: 2.2.4 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 | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 5aa6b36e..90014fb5 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( 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.2 + github.com/pelletier/go-toml/v2 v2.2.4 github.com/quic-go/quic-go v0.51.0 github.com/stretchr/testify v1.10.0 github.com/ugorji/go/codec v1.2.12 diff --git a/go.sum b/go.sum index 75b73d8c..f8d0534b 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,8 @@ 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/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 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.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= @@ -66,15 +66,12 @@ github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP 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= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=