From a210eea3bd1c3766d76968108dfcd83c331f549c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Fri, 21 Sep 2018 10:21:59 +0800 Subject: [PATCH 01/21] improve panic information when a catch-all wildcard conflict occurs (#1529) --- tree.go | 11 +++++++++-- tree_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/tree.go b/tree.go index b6530665..ada62ceb 100644 --- a/tree.go +++ b/tree.go @@ -193,9 +193,16 @@ func (n *node) addRoute(path string, handlers HandlersChain) { } } - panic("path segment '" + path + + pathSeg := path + if n.nType != catchAll { + pathSeg = strings.SplitN(path, "/", 2)[0] + } + prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path + panic("'" + pathSeg + + "' in new path '" + fullPath + "' conflicts with existing wildcard '" + n.path + - "' in path '" + fullPath + "'") + "' in existing prefix '" + prefix + + "'") } c := path[0] diff --git a/tree_test.go b/tree_test.go index 152f6331..a1b3bbe7 100644 --- a/tree_test.go +++ b/tree_test.go @@ -5,7 +5,9 @@ package gin import ( + "fmt" "reflect" + "regexp" "strings" "testing" ) @@ -653,3 +655,43 @@ func TestTreeInvalidNodeType(t *testing.T) { t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv) } } + +func TestTreeWildcardConflictEx(t *testing.T) { + conflicts := [...]struct { + route string + segPath string + existPath string + existSegPath string + }{ + {"/who/are/foo", "/foo", `/who/are/\*you`, `/\*you`}, + {"/who/are/foo/", "/foo/", `/who/are/\*you`, `/\*you`}, + {"/who/are/foo/bar", "/foo/bar", `/who/are/\*you`, `/\*you`}, + {"/conxxx", "xxx", `/con:tact`, `:tact`}, + {"/conooo/xxx", "ooo", `/con:tact`, `:tact`}, + } + + for _, conflict := range conflicts { + // I have to re-create a 'tree', because the 'tree' will be + // in an inconsistent state when the loop recovers from the + // panic which threw by 'addRoute' function. + tree := &node{} + routes := [...]string{ + "/con:tact", + "/who/are/*you", + "/who/foo/hello", + } + + for _, route := range routes { + tree.addRoute(route, fakeHandler(route)) + } + + recv := catchPanic(func() { + tree.addRoute(conflict.route, fakeHandler(conflict.route)) + }) + + if !regexp.MustCompile(fmt.Sprintf("'%s' in new path .* conflicts with existing wildcard '%s' in existing prefix '%s'", + conflict.segPath, conflict.existSegPath, conflict.existPath)).MatchString(fmt.Sprint(recv)) { + t.Fatalf("invalid wildcard conflict error (%v)", recv) + } + } +} From 5a75dc712705510ab39fe7c448e7ab11459ce5b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Sat, 22 Sep 2018 11:37:28 +0800 Subject: [PATCH 02/21] add release badge for readme (#1533) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 45bfce5a..6fd2dd34 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ [![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) Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. From ad53619b15fda20df57c16cf19fe83f4a428cefa Mon Sep 17 00:00:00 2001 From: Dustin Decker Date: Sun, 23 Sep 2018 00:15:23 -0700 Subject: [PATCH 03/21] Don't log requests (#1370) Fixes #1331 HTTP logging leaks sensitive request information. This PR removes HTTP request logging during panics. --- recovery.go | 8 ++++++-- recovery_test.go | 12 +++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/recovery.go b/recovery.go index 61c5bd53..6c28b4fa 100644 --- a/recovery.go +++ b/recovery.go @@ -39,8 +39,12 @@ func RecoveryWithWriter(out io.Writer) HandlerFunc { if err := recover(); err != nil { if logger != nil { stack := stack(3) - httprequest, _ := httputil.DumpRequest(c.Request, false) - logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset) + if IsDebugging() { + httprequest, _ := httputil.DumpRequest(c.Request, false) + logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset) + } else { + logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s", timeFormat(time.Now()), err, stack, reset) + } } c.AbortWithStatus(http.StatusInternalServerError) } diff --git a/recovery_test.go b/recovery_test.go index 53f4a071..7d422b74 100644 --- a/recovery_test.go +++ b/recovery_test.go @@ -24,9 +24,19 @@ func TestPanicInHandler(t *testing.T) { w := performRequest(router, "GET", "/recovery") // TEST assert.Equal(t, http.StatusInternalServerError, w.Code) - assert.Contains(t, buffer.String(), "GET /recovery") + assert.Contains(t, buffer.String(), "panic recovered") assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem") assert.Contains(t, buffer.String(), "TestPanicInHandler") + assert.NotContains(t, buffer.String(), "GET /recovery") + + // Debug mode prints the request + SetMode(DebugMode) + // RUN + w = performRequest(router, "GET", "/recovery") + // TEST + assert.Equal(t, http.StatusInternalServerError, w.Code) + assert.Contains(t, buffer.String(), "GET /recovery") + } // TestPanicWithAbort assert that panic has been recovered even if context.Abort was used. From b02e4f2ed68e1bd9598b8eced0b21c9aacfed5ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 25 Sep 2018 21:52:21 +0800 Subject: [PATCH 04/21] ci: fast finish when build failed (#1568) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 398c1409..f9e241d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ go: matrix: allow_failures: - go: master + fast_finish: true git: depth: 10 From fd599fcceadba48be39ded04d1e2fb3181b43e8c Mon Sep 17 00:00:00 2001 From: andrea Date: Tue, 25 Sep 2018 21:28:25 -0500 Subject: [PATCH 05/21] Make logger use a yellow background and a darkgray text for legibility (#1570) 1. Why is this change neccesary? White text on a yellow background was illegible with most terminal color schemes 2. How does it address the issue? The white text was replaced with a bash compatible dark gray while keeping the yellow background colour 3. What side effects does this change have? Resolves #1552 --- logger.go | 2 +- logger_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/logger.go b/logger.go index 1a8df601..5f986853 100644 --- a/logger.go +++ b/logger.go @@ -17,7 +17,7 @@ import ( var ( green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109}) white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109}) - yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109}) + yellow = string([]byte{27, 91, 57, 48, 59, 52, 51, 109}) red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109}) blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109}) magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109}) diff --git a/logger_test.go b/logger_test.go index 7dbbf7b1..6118cb04 100644 --- a/logger_test.go +++ b/logger_test.go @@ -85,7 +85,7 @@ func TestLogger(t *testing.T) { func TestColorForMethod(t *testing.T) { assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 52, 109}), colorForMethod("GET"), "get should be blue") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 54, 109}), colorForMethod("POST"), "post should be cyan") - assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 51, 109}), colorForMethod("PUT"), "put should be yellow") + assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 51, 109}), colorForMethod("PUT"), "put should be yellow") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), colorForMethod("DELETE"), "delete should be red") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), colorForMethod("PATCH"), "patch should be green") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 53, 109}), colorForMethod("HEAD"), "head should be magenta") @@ -96,7 +96,7 @@ func TestColorForMethod(t *testing.T) { func TestColorForStatus(t *testing.T) { assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), colorForStatus(http.StatusOK), "2xx should be green") assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 55, 109}), colorForStatus(http.StatusMovedPermanently), "3xx should be white") - assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 51, 109}), colorForStatus(http.StatusNotFound), "4xx should be yellow") + assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 51, 109}), colorForStatus(http.StatusNotFound), "4xx should be yellow") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), colorForStatus(2), "other things should be red") } From 834a2ec64ca1ce077e4bdeb5edec019df1c01e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Wed, 26 Sep 2018 13:49:11 +0800 Subject: [PATCH 06/21] feat: add go version judge when print debug warning log (#1572) * feat: add go version judge when print debug warning log * remove invalid statement * use one const --- debug.go | 18 +++++++++++++++++- debug_test.go | 24 +++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/debug.go b/debug.go index ce908dc7..4fd23fa1 100644 --- a/debug.go +++ b/debug.go @@ -8,8 +8,13 @@ import ( "bytes" "fmt" "html/template" + "runtime" + "strconv" + "strings" ) +const ginSupportMinGoVer = 6 + // IsDebugging returns true if the framework is running in debug mode. // Use SetMode(gin.ReleaseMode) to disable debug mode. func IsDebugging() bool { @@ -49,10 +54,21 @@ func debugPrint(format string, values ...interface{}) { } } +func getMinVer(v string) (uint64, error) { + first := strings.IndexByte(v, '.') + last := strings.LastIndexByte(v, '.') + if first == last { + return strconv.ParseUint(v[first+1:], 10, 64) + } + return strconv.ParseUint(v[first+1:last], 10, 64) +} + func debugPrintWARNINGDefault() { - debugPrint(`[WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon. + if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer { + debugPrint(`[WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon. `) + } debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. `) diff --git a/debug_test.go b/debug_test.go index 0b9d7910..c8a010ff 100644 --- a/debug_test.go +++ b/debug_test.go @@ -11,6 +11,7 @@ import ( "io" "log" "os" + "runtime" "sync" "testing" @@ -88,7 +89,13 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { debugPrintWARNINGDefault() SetMode(TestMode) }) - assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) + m, e := getMinVer(runtime.Version()) + assert.Nil(t, e) + if m <= ginSupportMinGoVer { + assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.\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) + } } func TestDebugPrintWARNINGNew(t *testing.T) { @@ -129,3 +136,18 @@ func captureOutput(f func()) string { writer.Close() return <-out } + +func TestGetMinVer(t *testing.T) { + var m uint64 + var e error + _, e = getMinVer("go1") + assert.NotNil(t, e) + m, e = getMinVer("go1.1") + assert.Equal(t, uint64(1), m) + assert.Nil(t, e) + m, e = getMinVer("go1.1.1") + assert.Nil(t, e) + assert.Equal(t, uint64(1), m) + _, e = getMinVer("go1.1.1.1") + assert.NotNil(t, e) +} From 402ef120e19218544152502d1ea20709ff5adbc8 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 27 Sep 2018 08:59:44 +0800 Subject: [PATCH 07/21] fix: fmt output log to os.Stderr (#1571) fix #1560 changes are breaking in App Engine. cc @giulianobr @philippgille --- debug.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debug.go b/debug.go index 4fd23fa1..c5e65b22 100644 --- a/debug.go +++ b/debug.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "html/template" + "os" "runtime" "strconv" "strings" @@ -50,7 +51,7 @@ func debugPrintLoadTemplate(tmpl *template.Template) { func debugPrint(format string, values ...interface{}) { if IsDebugging() { - fmt.Printf("[GIN-debug] "+format, values...) + fmt.Fprintf(os.Stderr, "[GIN-debug] "+format, values...) } } From b7314d943c81052dd7155499c7313d7524cb024c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Thu, 27 Sep 2018 09:42:41 +0800 Subject: [PATCH 08/21] chore: fix debug test error (#1574) --- debug_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/debug_test.go b/debug_test.go index c8a010ff..97ff166b 100644 --- a/debug_test.go +++ b/debug_test.go @@ -90,8 +90,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { SetMode(TestMode) }) m, e := getMinVer(runtime.Version()) - assert.Nil(t, e) - if m <= ginSupportMinGoVer { + if e == nil && m <= ginSupportMinGoVer { assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.\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 91a4459dd27a311c2b959708f328d60177fa4046 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 27 Sep 2018 09:58:47 +0800 Subject: [PATCH 09/21] remove allow fail flag (#1573) golang team revert the net/url issue: https://github.com/golang/go/issues/27302 --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f9e241d7..a765f37f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,6 @@ go: - master matrix: - allow_failures: - - go: master fast_finish: true git: From e9f187f60a27477e54c177f314db00d6a1abf062 Mon Sep 17 00:00:00 2001 From: James Pettyjohn Date: Sun, 30 Sep 2018 19:49:39 -0700 Subject: [PATCH 10/21] removed use of sync.pool from HandleContext and added test coverage (#1565) As per #1230 there is an issue when using HandleContext where the context of the request is returned to the context sync.Pool before the parent request has finished, causing context to be used in a non-thread safe manner. I've removed the bug by not entering the context back in the pool and leaving that to ServeHTTP. There was no test coverage for this function so I've also added the test to cover it. As the bug only happens when there are concurrent requests, the tests issues hundreds of concurrent requests. Without the bug fixed the tests do consistently recreate the error. --- gin.go | 1 - gin_integration_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 11f6d94a..92c24ba2 100644 --- a/gin.go +++ b/gin.go @@ -336,7 +336,6 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (engine *Engine) HandleContext(c *Context) { c.reset() engine.handleHTTPRequest(c) - engine.pool.Put(c) } func (engine *Engine) handleHTTPRequest(c *Context) { diff --git a/gin_integration_test.go b/gin_integration_test.go index 52f78842..12a943b0 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -12,6 +12,7 @@ import ( "net/http" "net/http/httptest" "os" + "sync" "testing" "time" @@ -119,6 +120,29 @@ func TestWithHttptestWithAutoSelectedPort(t *testing.T) { testRequest(t, ts.URL+"/example") } +func TestConcurrentHandleContext(t *testing.T) { + router := New() + router.GET("/", func(c *Context) { + c.Request.URL.Path = "/example" + router.HandleContext(c) + }) + router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) + + ts := httptest.NewServer(router) + defer ts.Close() + + var wg sync.WaitGroup + iterations := 200 + wg.Add(iterations) + for i := 0; i < iterations; i++ { + go func() { + testRequest(t, ts.URL+"/") + wg.Done() + }() + } + wg.Wait() +} + // func TestWithHttptestWithSpecifiedPort(t *testing.T) { // router := New() // router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) From fbdcbd22751c7174d4f363995fca296e4670726b Mon Sep 17 00:00:00 2001 From: zesani <7sin@outlook.co.th> Date: Tue, 9 Oct 2018 06:14:21 +0700 Subject: [PATCH 11/21] Update README.md (#1583) change "hava" to "have" --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6fd2dd34..95bd3204 100644 --- a/README.md +++ b/README.md @@ -1721,11 +1721,11 @@ type StructX struct { } type StructY struct { - Y StructX `form:"name_y"` // HERE hava form + Y StructX `form:"name_y"` // HERE have form } type StructZ struct { - Z *StructZ `form:"name_z"` // HERE hava form + Z *StructZ `form:"name_z"` // HERE have form } ``` From 6ab50f944ca52bdd4d982d0bec454d94bf7b802a Mon Sep 17 00:00:00 2001 From: andriikushch Date: Fri, 12 Oct 2018 01:31:31 +0200 Subject: [PATCH 12/21] replace deprecated HeaderMap with Header() (#1585) --- auth_test.go | 4 +-- context_17_test.go | 2 +- context_test.go | 64 +++++++++++++++++++++---------------------- render/render_test.go | 6 ++-- routes_test.go | 12 ++++---- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/auth_test.go b/auth_test.go index ab7e94be..197e9208 100644 --- a/auth_test.go +++ b/auth_test.go @@ -122,7 +122,7 @@ func TestBasicAuth401(t *testing.T) { assert.False(t, called) assert.Equal(t, http.StatusUnauthorized, w.Code) - assert.Equal(t, "Basic realm=\"Authorization Required\"", w.HeaderMap.Get("WWW-Authenticate")) + assert.Equal(t, "Basic realm=\"Authorization Required\"", w.Header().Get("WWW-Authenticate")) } func TestBasicAuth401WithCustomRealm(t *testing.T) { @@ -142,5 +142,5 @@ func TestBasicAuth401WithCustomRealm(t *testing.T) { assert.False(t, called) assert.Equal(t, http.StatusUnauthorized, w.Code) - assert.Equal(t, "Basic realm=\"My Custom \\\"Realm\\\"\"", w.HeaderMap.Get("WWW-Authenticate")) + assert.Equal(t, "Basic realm=\"My Custom \\\"Realm\\\"\"", w.Header().Get("WWW-Authenticate")) } diff --git a/context_17_test.go b/context_17_test.go index d2251904..5b9ebcdc 100644 --- a/context_17_test.go +++ b/context_17_test.go @@ -23,5 +23,5 @@ func TestContextRenderPureJSON(t *testing.T) { c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": ""}) assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\"}\n", w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } diff --git a/context_test.go b/context_test.go index 5a5bb6e1..2e972f18 100644 --- a/context_test.go +++ b/context_test.go @@ -628,7 +628,7 @@ func TestContextRenderJSON(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that the response is serialized as JSONP @@ -642,7 +642,7 @@ func TestContextRenderJSONP(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "x({\"foo\":\"bar\"})", w.Body.String()) - assert.Equal(t, "application/javascript; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that the response is serialized as JSONP @@ -656,7 +656,7 @@ func TestContextRenderJSONPWithoutCallback(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that no JSON is rendered if code is 204 @@ -668,7 +668,7 @@ func TestContextRenderNoContentJSON(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that the response is serialized as JSON @@ -682,7 +682,7 @@ func TestContextRenderAPIJSON(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) - assert.Equal(t, "application/vnd.api+json", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type")) } // Tests that no Custom JSON is rendered if code is 204 @@ -695,7 +695,7 @@ func TestContextRenderNoContentAPIJSON(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/vnd.api+json") + assert.Equal(t, w.Header().Get("Content-Type"), "application/vnd.api+json") } // Tests that the response is serialized as JSON @@ -708,7 +708,7 @@ func TestContextRenderIndentedJSON(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}", w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that no Custom JSON is rendered if code is 204 @@ -720,7 +720,7 @@ func TestContextRenderNoContentIndentedJSON(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that the response is serialized as Secure JSON @@ -734,7 +734,7 @@ func TestContextRenderSecureJSON(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "&&&START&&&[\"foo\",\"bar\"]", w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that no Custom JSON is rendered if code is 204 @@ -746,7 +746,7 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } func TestContextRenderNoContentAsciiJSON(t *testing.T) { @@ -757,7 +757,7 @@ func TestContextRenderNoContentAsciiJSON(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, "application/json", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json", w.Header().Get("Content-Type")) } // Tests that the response executes the templates @@ -773,7 +773,7 @@ func TestContextRenderHTML(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "Hello alexandernyquist", w.Body.String()) - assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } func TestContextRenderHTML2(t *testing.T) { @@ -797,7 +797,7 @@ func TestContextRenderHTML2(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "Hello alexandernyquist", w.Body.String()) - assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that no HTML is rendered if code is 204 @@ -811,7 +811,7 @@ func TestContextRenderNoContentHTML(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } // TestContextXML tests that the response is serialized as XML @@ -824,7 +824,7 @@ func TestContextRenderXML(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "bar", w.Body.String()) - assert.Equal(t, "application/xml; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that no XML is rendered if code is 204 @@ -836,7 +836,7 @@ func TestContextRenderNoContentXML(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, "application/xml; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type")) } // TestContextString tests that the response is returned @@ -849,7 +849,7 @@ func TestContextRenderString(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "test string 2", w.Body.String()) - assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that no String is rendered if code is 204 @@ -861,7 +861,7 @@ func TestContextRenderNoContentString(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) } // TestContextString tests that the response is returned @@ -875,7 +875,7 @@ func TestContextRenderHTMLString(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "string 3", w.Body.String()) - assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } // Tests that no HTML String is rendered if code is 204 @@ -888,7 +888,7 @@ func TestContextRenderNoContentHTMLString(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } // TestContextData tests that the response can be written from `bytesting` @@ -901,7 +901,7 @@ func TestContextRenderData(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, "foo,bar", w.Body.String()) - assert.Equal(t, "text/csv", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/csv", w.Header().Get("Content-Type")) } // Tests that no Custom Data is rendered if code is 204 @@ -913,7 +913,7 @@ func TestContextRenderNoContentData(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code) assert.Empty(t, w.Body.String()) - assert.Equal(t, "text/csv", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/csv", w.Header().Get("Content-Type")) } func TestContextRenderSSE(t *testing.T) { @@ -942,7 +942,7 @@ func TestContextRenderFile(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Contains(t, w.Body.String(), "func New() *Engine {") - assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) } // TestContextRenderYAML tests that the response is serialized as YAML @@ -955,7 +955,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.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type")) } // TestContextRenderProtoBuf tests that the response is serialized as ProtoBuf @@ -979,7 +979,7 @@ func TestContextRenderProtoBuf(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, string(protoData), w.Body.String()) - assert.Equal(t, "application/x-protobuf", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type")) } func TestContextHeaders(t *testing.T) { @@ -1062,7 +1062,7 @@ func TestContextNegotiationWithJSON(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } func TestContextNegotiationWithXML(t *testing.T) { @@ -1077,7 +1077,7 @@ func TestContextNegotiationWithXML(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "bar", w.Body.String()) - assert.Equal(t, "application/xml; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type")) } func TestContextNegotiationWithHTML(t *testing.T) { @@ -1095,7 +1095,7 @@ func TestContextNegotiationWithHTML(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "Hello gin", w.Body.String()) - assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } func TestContextNegotiationNotSupport(t *testing.T) { @@ -1131,7 +1131,7 @@ func TestContextNegotiationFormatWithAccept(t *testing.T) { assert.Empty(t, c.NegotiateFormat(MIMEJSON)) } -func TestContextNegotiationFormatCustum(t *testing.T) { +func TestContextNegotiationFormatCustom(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") @@ -1627,9 +1627,9 @@ func TestContextRenderDataFromReader(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, body, w.Body.String()) - assert.Equal(t, contentType, w.HeaderMap.Get("Content-Type")) - assert.Equal(t, fmt.Sprintf("%d", contentLength), w.HeaderMap.Get("Content-Length")) - assert.Equal(t, extraHeaders["Content-Disposition"], w.HeaderMap.Get("Content-Disposition")) + assert.Equal(t, contentType, w.Header().Get("Content-Type")) + assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length")) + assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition")) } type TestResponseRecorder struct { diff --git a/render/render_test.go b/render/render_test.go index 09ccc658..4c9b180d 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -480,7 +480,7 @@ func TestRenderReader(t *testing.T) { assert.NoError(t, err) assert.Equal(t, body, w.Body.String()) - assert.Equal(t, "image/png", w.HeaderMap.Get("Content-Type")) - assert.Equal(t, strconv.Itoa(len(body)), w.HeaderMap.Get("Content-Length")) - assert.Equal(t, headers["Content-Disposition"], w.HeaderMap.Get("Content-Disposition")) + assert.Equal(t, "image/png", w.Header().Get("Content-Type")) + assert.Equal(t, strconv.Itoa(len(body)), w.Header().Get("Content-Length")) + assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition")) } diff --git a/routes_test.go b/routes_test.go index 23e749e2..60f1c81b 100644 --- a/routes_test.go +++ b/routes_test.go @@ -267,7 +267,7 @@ func TestRouteStaticFile(t *testing.T) { 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.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) w3 := performRequest(router, "HEAD", "/using_static/"+filename) w4 := performRequest(router, "HEAD", "/result") @@ -285,7 +285,7 @@ func TestRouteStaticListingDir(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Contains(t, w.Body.String(), "gin.go") - assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type")) + assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) } // TestHandleHeadToDir - ensure the root/sub dir handles properly @@ -312,10 +312,10 @@ func TestRouterMiddlewareAndStatic(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) assert.Contains(t, w.Body.String(), "package gin") - assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type")) - assert.NotEqual(t, w.HeaderMap.Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST") - assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.HeaderMap.Get("Expires")) - assert.Equal(t, "Gin Framework", w.HeaderMap.Get("x-GIN")) + assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) + assert.NotEqual(t, w.Header().Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST") + 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")) } func TestRouteNotAllowedEnabled(t *testing.T) { From 268e30710b77e1cd48b25df235cf702dc8e942a2 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 14 Oct 2018 11:05:24 +0800 Subject: [PATCH 13/21] fix(Makefile): golint to new URL (#1592) as title. Just update the golint to new URL. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 51b9969f..ea7b4f7c 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ embedmd: .PHONY: lint lint: @hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - go get -u github.com/golang/lint/golint; \ + go get -u golang.org/x/lint/golint; \ fi for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done; From 01ca2530d4b6c44c718b00c06f0e1d092572d49e Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 14 Oct 2018 12:39:16 +0800 Subject: [PATCH 14/21] refactor(Makefile): allow overriding default go program (#1593) --- Makefile | 28 ++++++++++++++++++---------- coverage.sh | 13 ------------- 2 files changed, 18 insertions(+), 23 deletions(-) delete mode 100644 coverage.sh diff --git a/Makefile b/Makefile index ea7b4f7c..c20429a1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ +GO ?= go GOFMT ?= gofmt "-s" -PACKAGES ?= $(shell go list ./... | grep -v /vendor/) -VETPACKAGES ?= $(shell go list ./... | grep -v /vendor/ | grep -v /examples/) +PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/) +VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/ | grep -v /examples/) GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*") +TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | grep -v examples) all: install @@ -10,7 +12,14 @@ install: deps .PHONY: test test: - sh coverage.sh + echo "mode: count" > coverage.out + for d in $(TESTFOLDER); do \ + $(GO) test -v -covermode=count -coverprofile=profile.out $$d; \ + if [ -f profile.out ]; then \ + cat profile.out | grep -v "mode:" >> coverage.out; \ + rm profile.out; \ + fi; \ + done .PHONY: fmt fmt: @@ -18,7 +27,6 @@ fmt: .PHONY: fmt-check fmt-check: - # get all go files and run go fmt on them @diff=$$($(GOFMT) -d $(GOFILES)); \ if [ -n "$$diff" ]; then \ echo "Please run 'make fmt' and commit the result:"; \ @@ -27,14 +35,14 @@ fmt-check: fi; vet: - go vet $(VETPACKAGES) + $(GO) vet $(VETPACKAGES) deps: @hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - go get -u github.com/kardianos/govendor; \ + $(GO) get -u github.com/kardianos/govendor; \ fi @hash embedmd > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - go get -u github.com/campoy/embedmd; \ + $(GO) get -u github.com/campoy/embedmd; \ fi embedmd: @@ -43,20 +51,20 @@ embedmd: .PHONY: lint lint: @hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - go get -u golang.org/x/lint/golint; \ + $(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; \ + $(GO) get -u github.com/client9/misspell/cmd/misspell; \ fi misspell -error $(GOFILES) .PHONY: misspell misspell: @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - go get -u github.com/client9/misspell/cmd/misspell; \ + $(GO) get -u github.com/client9/misspell/cmd/misspell; \ fi misspell -w $(GOFILES) diff --git a/coverage.sh b/coverage.sh deleted file mode 100644 index 4d1ee036..00000000 --- a/coverage.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -e - -echo "mode: count" > coverage.out - -for d in $(go list ./... | grep -E 'gin$|binding$|render$' | grep -v 'examples'); do - go test -v -covermode=count -coverprofile=profile.out $d - if [ -f profile.out ]; then - cat profile.out | grep -v "mode:" >> coverage.out - rm profile.out - fi -done From 523435e5245faade0a43a33bd3faab32456bab32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Mon, 15 Oct 2018 12:52:51 +0800 Subject: [PATCH 15/21] attempt to support go module (#1569) * support go module * update golint package url * update golint --- .travis.yml | 10 +++++++++- Makefile | 6 ++++++ go.mod | 13 ++++++++----- go.sum | 26 ++++++++++++++++---------- tools.go | 25 +++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 tools.go diff --git a/.travis.yml b/.travis.yml index a765f37f..2eeb0b3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,12 +11,20 @@ go: matrix: fast_finish: true + include: + - go: 1.11.x + env: GO111MODULE=on git: depth: 10 +before_install: + - if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi + install: - - make install + - if [[ "${GO111MODULE}" = "on" ]]; then go mod download; else make install; fi + - if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi + - if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi go_import_path: github.com/gin-gonic/gin diff --git a/Makefile b/Makefile index c20429a1..b698ac09 100644 --- a/Makefile +++ b/Makefile @@ -68,3 +68,9 @@ misspell: $(GO) get -u github.com/client9/misspell/cmd/misspell; \ fi misspell -w $(GOFILES) + +.PHONY: tools +tools: + go install golang.org/x/lint/golint; \ + go install github.com/client9/misspell/cmd/misspell; \ + go install github.com/campoy/embedmd; diff --git a/go.mod b/go.mod index bd4ad975..0797b934 100644 --- a/go.mod +++ b/go.mod @@ -1,25 +1,28 @@ module github.com/gin-gonic/gin require ( - github.com/davecgh/go-spew v0.0.0-20180221232628-8991bc29aa16 // indirect + github.com/campoy/embedmd v0.0.0-20171205015432-c59ce00e0296 + github.com/client9/misspell v0.3.4 + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66 github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b github.com/golang/protobuf v1.2.0 github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 - github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b + github.com/json-iterator/go v1.1.5 github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227 github.com/mattn/go-isatty v0.0.3 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 - github.com/thinkerou/favicon v0.0.0-20170710140520-94a442a49da6 + github.com/thinkerou/favicon v0.1.0 github.com/ugorji/go v1.1.1 - golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b + golang.org/x/crypto v0.0.0-20180927165925-5295e8364332 + golang.org/x/lint v0.0.0-20180702182130-06c8688daad7 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f - golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e // indirect + golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 // indirect google.golang.org/grpc v1.15.0 gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v8 v8.18.2 diff --git a/go.sum b/go.sum index 3382beed..2d307e7e 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/campoy/embedmd v0.0.0-20171205015432-c59ce00e0296 h1:tRsilif6pbtt+PX6uRoyGd+qR+4ZPucFZLHlc3Ak6z8= +github.com/campoy/embedmd v0.0.0-20171205015432-c59ce00e0296/go.mod h1:/dBk8ICkslPCmyRdn4azP+QvBxL6Eg3EYxUGI9xMMFw= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/davecgh/go-spew v0.0.0-20180221232628-8991bc29aa16 h1:HqkufMBR7waVfFFRABWqHa1WgTvjtVDJTLJe3CR576A= -github.com/davecgh/go-spew v0.0.0-20180221232628-8991bc29aa16/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/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66 h1:QnnoVdChKs+GeTvN4rPYTW6b5U6M3HMEvQ/+x4IGtfY= github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66/go.mod h1:kTEh6M2J/mh7nsskr28alwLCXm/DSG5OSA/o31yy2XU= github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY= @@ -10,14 +13,15 @@ github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b h1:dm/NYytoj7p8J github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b/go.mod h1:vwfeXwKgEIWq63oVfwaBjoByS4dZzYbHHROHjV4IjNY= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 h1:cW/amwGEJK5MSKntPXRjX4dxs/nGxGT8gXKIsKFmHGc= github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15/go.mod h1:Fdm/oWRW+CH8PRbLntksCNtmcCBximKPkVQYvmMl80k= -github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b h1:X61dhFTE1Au92SvyF8HyAwdjWqiSdfBgFR7wTxC0+uU= -github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227 h1:KIaAZ/V+/0/6BOULrmBQ9T1ed8BkKqGIjIKW923nJuo= github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227/go.mod h1:ruMr5t05gVho4tuDv0PbI0Bb8nOxc/5Y6JzRHe/yfA0= @@ -31,12 +35,13 @@ 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/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/thinkerou/favicon v0.0.0-20170710140520-94a442a49da6 h1:d/LEgOfWe+AlOCz/kzmkvlO+gq9LRGhjSHqt2nx8Muc= -github.com/thinkerou/favicon v0.0.0-20170710140520-94a442a49da6/go.mod h1:HL7Pap5kOluZv1ku34pZo/AJ44GaxMEPFZ3pmuexV2s= +github.com/thinkerou/favicon v0.1.0 h1:eWMISKTpHq2G8HOuKn7ydD55j5DDehx94b0C2y8ABMs= +github.com/thinkerou/favicon v0.1.0/go.mod h1:HL7Pap5kOluZv1ku34pZo/AJ44GaxMEPFZ3pmuexV2s= github.com/ugorji/go v1.1.1 h1:gmervu+jDMvXTbcHQ0pd2wee85nEoE0BsVyEuzkfK8w= github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= -golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ= -golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180927165925-5295e8364332 h1:hvQVdF6P9DX4OiKA5tpehlG6JsgzmyQiThG7q5Bn3UQ= +golang.org/x/crypto v0.0.0-20180927165925-5295e8364332/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7 h1:00BeQWmeaGazuOrq8Q5K5d3/cHaGuFrZzpaHBXfrsUA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -44,10 +49,11 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0= +golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52 h1:JG/0uqcGdTNgq7FdU+61l5Pdmb8putNZlXb65bJBROs= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= diff --git a/tools.go b/tools.go new file mode 100644 index 00000000..9f96406a --- /dev/null +++ b/tools.go @@ -0,0 +1,25 @@ +// 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. + +// +build tools + +// This file exists to cause `go mod` and `go get` to believe these tools +// are dependencies, even though they are not runtime dependencies of any +// gin package. This means they will appear in `go.mod` file, but will not +// be a part of the build. + +package gin + +import ( + _ "github.com/campoy/embedmd" + _ "github.com/client9/misspell/cmd/misspell" + _ "github.com/dustin/go-broadcast" + _ "github.com/gin-gonic/autotls" + _ "github.com/jessevdk/go-assets" + _ "github.com/manucorporat/stats" + _ "github.com/thinkerou/favicon" + _ "golang.org/x/crypto/acme/autocert" + _ "golang.org/x/lint/golint" + _ "google.golang.org/grpc" +) From 98082fd590798eda8bdada82fd1550dfe6941964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Mon, 15 Oct 2018 13:01:44 +0800 Subject: [PATCH 16/21] document: add docs dir and middleware document (#1521) * init docs dir * add middleware document * fix indent * update docs --- docs/how-to-build-an-effective-middleware.md | 137 +++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 docs/how-to-build-an-effective-middleware.md diff --git a/docs/how-to-build-an-effective-middleware.md b/docs/how-to-build-an-effective-middleware.md new file mode 100644 index 00000000..db04428c --- /dev/null +++ b/docs/how-to-build-an-effective-middleware.md @@ -0,0 +1,137 @@ +# How to build one effective middleware? + +## Consitituent part + +The middleware has two parts: + + - part one is what is executed once, when you initalize your middleware. That's where you set up all the global objects, logicals etc. Everything that happens one per application lifetime. + + - part two is what executes on every request. For example, a database middleware you simply inject your "global" database object into the context. Once it's inside the context, you can retrieve it from within other middlewares and your handler furnction. + +```go +func funcName(params string) gin.HandlerFunc { + // <--- + // This is part one + // ---> + // The follow code is an example + if err := check(params); err != nil { + panic(err) + } + + return func(c *gin.Context) { + // <--- + // This is part two + // ---> + // The follow code is an example + c.Set("TestVar", params) + c.Next() + } +} +``` + +## Execution process + +Firstly, we have the follow example code: + +```go +func main() { + router := gin.Default() + + router.Use(globalMiddleware()) + + router.GET("/rest/n/api/*some", mid1(), mid2(), handler) + + router.Run() +} + +func globalMiddleware() gin.HandlerFunc { + fmt.Println("globalMiddleware...1") + + return func(c *gin.Context) { + fmt.Println("globalMiddleware...2") + c.Next() + fmt.Println("globalMiddleware...3") + } +} + +func handler(c *gin.Context) { + fmt.Println("exec handler.") +} + +func mid1() gin.HandlerFunc { + fmt.Println("mid1...1") + + return func(c *gin.Context) { + + fmt.Println("mid1...2") + c.Next() + fmt.Println("mid1...3") + } +} + +func mid2() gin.HandlerFunc { + fmt.Println("mid2...1") + + return func(c *gin.Context) { + fmt.Println("mid2...2") + c.Next() + fmt.Println("mid2...3") + } +} +``` + +According to [Consitituent part](#consitituent-part) said, when we run the gin process, **part one** will execute firstly and will print the follow information: + +```go +globalMiddleware...1 +mid1...1 +mid2...1 +``` + +And init order are: + +```go +globalMiddleware...1 + | + v +mid1...1 + | + v +mid2...1 +``` + +When we curl one request `curl -v localhost:8080/rest/n/api/some`, **part two** will execute their middleware and output the following information: + +```go +globalMiddleware...2 +mid1...2 +mid2...2 +exec handler. +mid2...3 +mid1...3 +globalMiddleware...3 +``` + +In other words, run order are: + +```go +globalMiddleware...2 + | + v +mid1...2 + | + v +mid2...2 + | + v +exec handler. + | + v +mid2...3 + | + v +mid1...3 + | + v +globalMiddleware...3 +``` From 524757b81c99e51a64e6ecc7ee0c181abc39bd79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Mon, 15 Oct 2018 20:24:32 +0800 Subject: [PATCH 17/21] vendor: upgrade some dependency package version (#1596) ref https://github.com/gin-gonic/gin/pull/1569#issuecomment-429731722 --- go.mod | 10 +++++----- go.sum | 19 ++++++++++--------- vendor/vendor.json | 43 +++++++++++++++++++++++++------------------ 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/go.mod b/go.mod index 0797b934..ef4103fd 100644 --- a/go.mod +++ b/go.mod @@ -11,18 +11,18 @@ require ( github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 github.com/json-iterator/go v1.1.5 github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227 - github.com/mattn/go-isatty v0.0.3 + github.com/mattn/go-isatty v0.0.4 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 github.com/thinkerou/favicon v0.1.0 github.com/ugorji/go v1.1.1 - golang.org/x/crypto v0.0.0-20180927165925-5295e8364332 - golang.org/x/lint v0.0.0-20180702182130-06c8688daad7 - golang.org/x/net v0.0.0-20180826012351-8a410e7b638d + golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e + golang.org/x/lint v0.0.0-20181011164241-5906bd5c48cd + golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f - golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 // indirect + golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba // indirect google.golang.org/grpc v1.15.0 gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v8 v8.18.2 diff --git a/go.sum b/go.sum index 2d307e7e..2ef7f13b 100644 --- a/go.sum +++ b/go.sum @@ -13,7 +13,6 @@ github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b h1:dm/NYytoj7p8J github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b/go.mod h1:vwfeXwKgEIWq63oVfwaBjoByS4dZzYbHHROHjV4IjNY= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= @@ -25,8 +24,8 @@ github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227 h1:KIaAZ/V+/0/6BOULrmBQ9T1ed8BkKqGIjIKW923nJuo= github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227/go.mod h1:ruMr5t05gVho4tuDv0PbI0Bb8nOxc/5Y6JzRHe/yfA0= -github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 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.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= @@ -39,18 +38,20 @@ github.com/thinkerou/favicon v0.1.0 h1:eWMISKTpHq2G8HOuKn7ydD55j5DDehx94b0C2y8AB github.com/thinkerou/favicon v0.1.0/go.mod h1:HL7Pap5kOluZv1ku34pZo/AJ44GaxMEPFZ3pmuexV2s= github.com/ugorji/go v1.1.1 h1:gmervu+jDMvXTbcHQ0pd2wee85nEoE0BsVyEuzkfK8w= github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= -golang.org/x/crypto v0.0.0-20180927165925-5295e8364332 h1:hvQVdF6P9DX4OiKA5tpehlG6JsgzmyQiThG7q5Bn3UQ= -golang.org/x/crypto v0.0.0-20180927165925-5295e8364332/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7 h1:00BeQWmeaGazuOrq8Q5K5d3/cHaGuFrZzpaHBXfrsUA= +golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e h1:IzypfodbhbnViNUO/MEh0FzCUooG97cIGfdggUrUSyU= +golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= +golang.org/x/lint v0.0.0-20181011164241-5906bd5c48cd h1:cgsAvzdqkDKdI02tIvDjO225vDPHMDCgfKqx5KEVI7U= +golang.org/x/lint v0.0.0-20181011164241-5906bd5c48cd/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1 h1:Y/KGZSOdz/2r0WJ9Mkmz6NJBusp0kiNx1Cn82lzJQ6w= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0= -golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba h1:nZJIJPGow0Kf9bU9QTc1U6OXbs/7Hu4e+cNv+hxH+Zc= +golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52 h1:JG/0uqcGdTNgq7FdU+61l5Pdmb8putNZlXb65bJBROs= diff --git a/vendor/vendor.json b/vendor/vendor.json index 86df11be..af1a0148 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -6,7 +6,9 @@ "checksumSHA1": "CSPbwbyzqA6sfORicn4HFtIhF/c=", "path": "github.com/davecgh/go-spew/spew", "revision": "8991bc29aa16c548c550c7ff78260e27b9ab7c73", - "revisionTime": "2018-02-21T22:46:20Z" + "revisionTime": "2018-02-21T22:46:20Z", + "version": "v1.1", + "versionExact": "v1.1.1" }, { "checksumSHA1": "QeKwBtN2df+j+4stw3bQJ6yO4EY=", @@ -15,12 +17,12 @@ "revisionTime": "2017-01-09T09:34:21Z" }, { - "checksumSHA1": "Pyou8mceOASSFxc7GeXZuVdSMi0=", + "checksumSHA1": "mE9XW26JSpe4meBObM6J/Oeq0eg=", "path": "github.com/golang/protobuf/proto", - "revision": "b4deda0973fb4c70b50d226b1af49f3da59f5265", - "revisionTime": "2018-04-30T18:52:41Z", - "version": "v1.1.0", - "versionExact": "v1.1.0" + "revision": "aa810b61a9c79d51363740d207bb46cf8e620ed5", + "revisionTime": "2018-08-14T21:14:27Z", + "version": "v1.2", + "versionExact": "v1.2.0" }, { "checksumSHA1": "WqeEgS7pqqkwK8mlrAZmDgtWJMY=", @@ -31,19 +33,19 @@ "versionExact": "v1.1.5" }, { - "checksumSHA1": "y/A5iuvwjytQE2CqVuphQRXR2nI=", + "checksumSHA1": "w5RcOnfv5YDr3j2bd1YydkPiZx4=", "path": "github.com/mattn/go-isatty", - "revision": "0360b2af4f38e8d38c7fce2a9f4e702702d73a39", - "revisionTime": "2017-09-25T05:34:41Z", - "version": "v0.0.3", - "versionExact": "v0.0.3" + "revision": "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c", + "revisionTime": "2017-11-07T05:05:31Z", + "version": "v0.0", + "versionExact": "v0.0.4" }, { "checksumSHA1": "c6pbpF7eowwO59phRTpF8cQ80Z0=", "path": "github.com/stretchr/testify/assert", "revision": "f35b8ab0b5a2cef36673838d662e249dd9c94686", "revisionTime": "2018-05-06T18:05:49Z", - "version": "v1.2.2", + "version": "v1.2", "versionExact": "v1.2.2" }, { @@ -51,15 +53,20 @@ "path": "github.com/ugorji/go/codec", "revision": "b4c50a2b199d93b13dc15e78929cfb23bfdf21ab", "revisionTime": "2018-04-07T10:07:33Z", - "version": "v1.1.1", + "version": "v1.1", "versionExact": "v1.1.1" }, { - "checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=", - "comment": "release-branch.go1.7", + "checksumSHA1": "GtamqiJoL7PGHsN454AoffBFMa8=", "path": "golang.org/x/net/context", - "revision": "d4c55e66d8c3a2f3382d264b08e3e3454a66355a", - "revisionTime": "2016-10-18T08:54:36Z" + "revision": "49bb7cea24b1df9410e1712aa6433dae904ff66a", + "revisionTime": "2018-10-11T05:27:23Z" + }, + { + "checksumSHA1": "SiJNkx+YGtq3Gtr6Ldu6OW83O+U=", + "path": "golang.org/x/sys/unix", + "revision": "fa43e7bc11baaae89f3f902b2b4d832b68234844", + "revisionTime": "2018-10-11T14:35:51Z" }, { "checksumSHA1": "P/k5ZGf0lEBgpKgkwy++F7K1PSg=", @@ -74,7 +81,7 @@ "path": "gopkg.in/yaml.v2", "revision": "5420a8b6744d3b0345ab293f6fcba19c978f1183", "revisionTime": "2018-03-28T19:50:20Z", - "version": "v2.2.1", + "version": "v2.2", "versionExact": "v2.2.1" } ], From cfa092f4f045d004b4c03d5b69af8bab7ed5fc1b Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Tue, 16 Oct 2018 03:48:41 +0300 Subject: [PATCH 18/21] Fix LoadHTML* tests (#1559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Digging into the test code base I've found out that some of the tests for `LoadHTML*` methods are not reliable and efficient. They use timeouts to be sure that goroutine with the server has started. And even more, in old implementation, the server started only once – all the new instances silently failed due to the occupied network port. Here is a short overview of the proposed changes: - it's not necessary to rely on timeouts, the server starts listening synchronously and returns control when it is ready - once the server is run, it's stopped after a test passes - dry out http server setup - magic with empty closure return is eliminated - preserve router.RunTLS coverage with integration tests --- gin_integration_test.go | 26 ++++- gin_test.go | 252 ++++++++++++++++++++++------------------ 2 files changed, 165 insertions(+), 113 deletions(-) diff --git a/gin_integration_test.go b/gin_integration_test.go index 12a943b0..038c8b7c 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -6,6 +6,7 @@ package gin import ( "bufio" + "crypto/tls" "fmt" "io/ioutil" "net" @@ -20,7 +21,14 @@ import ( ) func testRequest(t *testing.T, url string) { - resp, err := http.Get(url) + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + client := &http.Client{Transport: tr} + + resp, err := client.Get(url) assert.NoError(t, err) defer resp.Body.Close() @@ -45,6 +53,22 @@ func TestRunEmpty(t *testing.T) { testRequest(t, "http://localhost:8080/example") } +func TestRunTLS(t *testing.T) { + router := New() + go func() { + router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) + + assert.NoError(t, router.RunTLS(":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.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) + testRequest(t, "https://localhost:8443/example") +} + func TestRunEmptyWithEnv(t *testing.T) { os.Setenv("PORT", "3123") router := New() diff --git a/gin_test.go b/gin_test.go index 95b9cc16..353c9be1 100644 --- a/gin_test.go +++ b/gin_test.go @@ -10,6 +10,7 @@ import ( "html/template" "io/ioutil" "net/http" + "net/http/httptest" "reflect" "testing" "time" @@ -22,105 +23,105 @@ func formatAsDate(t time.Time) string { return fmt.Sprintf("%d/%02d/%02d", year, month, day) } -func setupHTMLFiles(t *testing.T, mode string, tls bool) func() { - go func() { - SetMode(mode) - router := New() - router.Delims("{[{", "}]}") - router.SetFuncMap(template.FuncMap{ - "formatAsDate": formatAsDate, +func setupHTMLFiles(t *testing.T, mode string, tls bool, loadMethod func(*Engine)) *httptest.Server { + SetMode(mode) + router := New() + router.Delims("{[{", "}]}") + router.SetFuncMap(template.FuncMap{ + "formatAsDate": formatAsDate, + }) + loadMethod(router) + router.GET("/test", func(c *Context) { + 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{}{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), }) - router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") - router.GET("/test", func(c *Context) { - 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{}{ - "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), - }) - }) - if tls { - // these files generated by `go run $GOROOT/src/crypto/tls/generate_cert.go --host 127.0.0.1` - router.RunTLS(":9999", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem") - } else { - router.Run(":8888") - } - }() - t.Log("waiting 1 second for server startup") - time.Sleep(1 * time.Second) - return func() {} + }) + + var ts *httptest.Server + + if tls { + ts = httptest.NewTLSServer(router) + } else { + ts = httptest.NewServer(router) + } + + return ts } -func setupHTMLGlob(t *testing.T, mode string, tls bool) func() { - go func() { - SetMode(mode) - router := New() - router.Delims("{[{", "}]}") - router.SetFuncMap(template.FuncMap{ - "formatAsDate": formatAsDate, - }) - router.LoadHTMLGlob("./testdata/template/*") - router.GET("/test", func(c *Context) { - 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{}{ - "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), - }) - }) - if tls { - // these files generated by `go run $GOROOT/src/crypto/tls/generate_cert.go --host 127.0.0.1` - router.RunTLS(":9999", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem") - } else { - router.Run(":8888") - } - }() - t.Log("waiting 1 second for server startup") - time.Sleep(1 * time.Second) - return func() {} -} +func TestLoadHTMLGlobDebugMode(t *testing.T) { + ts := setupHTMLFiles( + t, + DebugMode, + false, + func(router *Engine) { + router.LoadHTMLGlob("./testdata/template/*") + }, + ) + defer ts.Close() -func TestLoadHTMLGlob(t *testing.T) { - td := setupHTMLGlob(t, DebugMode, false) - res, err := http.Get("http://127.0.0.1:8888/test") + res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) - - td() } -func TestLoadHTMLGlob2(t *testing.T) { - td := setupHTMLGlob(t, TestMode, false) - res, err := http.Get("http://127.0.0.1:8888/test") +func TestLoadHTMLGlobTestMode(t *testing.T) { + ts := setupHTMLFiles( + t, + TestMode, + false, + func(router *Engine) { + router.LoadHTMLGlob("./testdata/template/*") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) - - td() } -func TestLoadHTMLGlob3(t *testing.T) { - td := setupHTMLGlob(t, ReleaseMode, false) - res, err := http.Get("http://127.0.0.1:8888/test") +func TestLoadHTMLGlobReleaseMode(t *testing.T) { + ts := setupHTMLFiles( + t, + ReleaseMode, + false, + func(router *Engine) { + router.LoadHTMLGlob("./testdata/template/*") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) - - td() } func TestLoadHTMLGlobUsingTLS(t *testing.T) { - td := setupHTMLGlob(t, DebugMode, true) + ts := setupHTMLFiles( + t, + DebugMode, + true, + func(router *Engine) { + router.LoadHTMLGlob("./testdata/template/*") + }, + ) + defer ts.Close() + // Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error tr := &http.Transport{ TLSClientConfig: &tls.Config{ @@ -128,29 +129,33 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) { }, } client := &http.Client{Transport: tr} - res, err := client.Get("https://127.0.0.1:9999/test") + res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) - - td() } func TestLoadHTMLGlobFromFuncMap(t *testing.T) { - time.Now() - td := setupHTMLGlob(t, DebugMode, false) - res, err := http.Get("http://127.0.0.1:8888/raw") + ts := setupHTMLFiles( + t, + DebugMode, + false, + func(router *Engine) { + router.LoadHTMLGlob("./testdata/template/*") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "Date: 2017/07/01\n", string(resp)) - - td() } func init() { @@ -164,59 +169,77 @@ func TestCreateEngine(t *testing.T) { assert.Empty(t, router.Handlers) } -// func TestLoadHTMLDebugMode(t *testing.T) { -// router := New() -// SetMode(DebugMode) -// router.LoadHTMLGlob("*.testtmpl") -// r := router.HTMLRender.(render.HTMLDebug) -// assert.Empty(t, r.Files) -// assert.Equal(t, "*.testtmpl", r.Glob) -// -// router.LoadHTMLFiles("index.html.testtmpl", "login.html.testtmpl") -// r = router.HTMLRender.(render.HTMLDebug) -// assert.Empty(t, r.Glob) -// assert.Equal(t, []string{"index.html", "login.html"}, r.Files) -// SetMode(TestMode) -// } +func TestLoadHTMLFilesTestMode(t *testing.T) { + ts := setupHTMLFiles( + t, + TestMode, + false, + func(router *Engine) { + router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") + }, + ) + defer ts.Close() -func TestLoadHTMLFiles(t *testing.T) { - td := setupHTMLFiles(t, TestMode, false) - res, err := http.Get("http://127.0.0.1:8888/test") + res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) - td() } -func TestLoadHTMLFiles2(t *testing.T) { - td := setupHTMLFiles(t, DebugMode, false) - res, err := http.Get("http://127.0.0.1:8888/test") +func TestLoadHTMLFilesDebugMode(t *testing.T) { + ts := setupHTMLFiles( + t, + DebugMode, + false, + func(router *Engine) { + router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) - td() } -func TestLoadHTMLFiles3(t *testing.T) { - td := setupHTMLFiles(t, ReleaseMode, false) - res, err := http.Get("http://127.0.0.1:8888/test") +func TestLoadHTMLFilesReleaseMode(t *testing.T) { + ts := setupHTMLFiles( + t, + ReleaseMode, + false, + func(router *Engine) { + router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) - td() } func TestLoadHTMLFilesUsingTLS(t *testing.T) { - td := setupHTMLFiles(t, TestMode, true) + ts := setupHTMLFiles( + t, + TestMode, + true, + func(router *Engine) { + router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") + }, + ) + defer ts.Close() + // Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error tr := &http.Transport{ TLSClientConfig: &tls.Config{ @@ -224,28 +247,33 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) { }, } client := &http.Client{Transport: tr} - res, err := client.Get("https://127.0.0.1:9999/test") + res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) - td() } func TestLoadHTMLFilesFuncMap(t *testing.T) { - time.Now() - td := setupHTMLFiles(t, TestMode, false) - res, err := http.Get("http://127.0.0.1:8888/raw") + ts := setupHTMLFiles( + t, + TestMode, + false, + func(router *Engine) { + router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") + }, + ) + defer ts.Close() + + res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "Date: 2017/07/01\n", string(resp)) - - td() } func TestAddRoute(t *testing.T) { From 333bac5f94a1bd3237996fbcae6e78fee8dbc5c6 Mon Sep 17 00:00:00 2001 From: "A. F" Date: Wed, 17 Oct 2018 09:40:57 +0200 Subject: [PATCH 19/21] add example to set and get cookies (#1599) --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 95bd3204..5c14c440 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [Try to bind body into different structs](#try-to-bind-body-into-different-structs) - [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) - [Testing](#testing) - [Users](#users) @@ -1880,6 +1881,35 @@ func main() { } ``` +### 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() +} +``` + ## Testing From a1a32562de0fe61b17aab42cd4fa7847ae814cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Fri, 19 Oct 2018 11:06:23 +0800 Subject: [PATCH 20/21] add gin user - photoprism (#1601) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5c14c440..1b5fb493 100644 --- a/README.md +++ b/README.md @@ -1964,3 +1964,4 @@ Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framewor * [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go. * [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. From dbc330b804052d7db230729c15d041cbb775e7b6 Mon Sep 17 00:00:00 2001 From: Ismail Gjevori Date: Mon, 22 Oct 2018 17:01:14 +0200 Subject: [PATCH 21/21] Pass MaxMultipartMemory when FormFile is called (#1600) When `gin.Context.FormFile("...")` is called the `engine.MaxMultipartMemory` is never used. This PR makes sure that the `MaxMultipartMemory` is passed and removes 2 calls to `http.Request.ParseForm` since they are called from `http.Request.ParseMultipartForm` --- context.go | 7 +++++-- context_test.go | 13 +++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 809902a3..c8a59bb0 100644 --- a/context.go +++ b/context.go @@ -414,7 +414,6 @@ func (c *Context) PostFormArray(key string) []string { // a boolean value whether at least one value exists for the given key. func (c *Context) GetPostFormArray(key string) ([]string, bool) { req := c.Request - req.ParseForm() req.ParseMultipartForm(c.engine.MaxMultipartMemory) if values := req.PostForm[key]; len(values) > 0 { return values, true @@ -437,7 +436,6 @@ func (c *Context) PostFormMap(key string) map[string]string { // whether at least one value exists for the given key. func (c *Context) GetPostFormMap(key string) (map[string]string, bool) { req := c.Request - req.ParseForm() req.ParseMultipartForm(c.engine.MaxMultipartMemory) dicts, exist := c.get(req.PostForm, key) @@ -465,6 +463,11 @@ func (c *Context) get(m map[string][]string, key string) (map[string]string, boo // FormFile returns the first file for the provided form key. func (c *Context) FormFile(name string) (*multipart.FileHeader, error) { + if c.Request.MultipartForm == nil { + if err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil { + return nil, err + } + } _, fh, err := c.Request.FormFile(name) return fh, err } diff --git a/context_test.go b/context_test.go index 2e972f18..fb492e02 100644 --- a/context_test.go +++ b/context_test.go @@ -84,6 +84,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)