From ce26751a5a3ed13e9a6aa010d9a7fa767de91b8c Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Thu, 28 Jan 2016 02:32:44 +0900 Subject: [PATCH 001/113] add import path to .travis.yml --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 695f0b7e..e4f7e05e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,8 @@ go: - 1.4.2 - tip +go_import_path: github.com/gin-gonic/gin + script: - go get golang.org/x/tools/cmd/cover - go get github.com/mattn/goveralls From 781cbd19f02c42795bdc3adc2fda287d95f91500 Mon Sep 17 00:00:00 2001 From: Ammar Bandukwala Date: Mon, 8 May 2017 19:04:22 -0500 Subject: [PATCH 002/113] panic if err is nil on c.Error A panic here provides a more informative stack trace than the panic which would otherwise occur while errors are being collected. --- context.go | 4 ++++ context_test.go | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/context.go b/context.go index 7b57150d..dd0dc5dc 100644 --- a/context.go +++ b/context.go @@ -144,7 +144,11 @@ func (c *Context) AbortWithError(code int, err error) *Error { // It's a good idea to call Error for each error that occurred during the resolution of a request. // A middleware can be used to collect all the errors // and push them to a database together, print a log, or append it in the HTTP response. +// Error will panic if err is nil. func (c *Context) Error(err error) *Error { + if err == nil { + panic("err is nil") + } var parsedError *Error switch err.(type) { case *Error: diff --git a/context_test.go b/context_test.go index 0feeca89..b4c1f08d 100644 --- a/context_test.go +++ b/context_test.go @@ -852,6 +852,13 @@ func TestContextError(t *testing.T) { assert.Equal(t, c.Errors[1].Type, ErrorTypePublic) assert.Equal(t, c.Errors.Last(), c.Errors[1]) + + defer func() { + if recover() == nil { + t.Error("didn't panic") + } + }() + c.Error(nil) } func TestContextTypedError(t *testing.T) { From 0cbf0f48aaa3573141922d9dbdc57a15ea3aa626 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 11:58:19 +0200 Subject: [PATCH 003/113] docs(readme): update contribution guide - change new prs base branch from develop to master --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 029606b7..76ec40db 100644 --- a/README.md +++ b/README.md @@ -963,7 +963,7 @@ func main() { - Please provide source code and commit sha if you found a bug. - Review existing issues and provide feedback or react to them. - With pull requests: - - Open your pull request against develop + - Open your pull request against master - Your pull request should have no more than two commits, if not you should squash them. - It should pass all tests in the available continuous integrations systems such as TravisCI. - You should add/modify tests to cover your proposed code changes. From 55dd6364cbdf2a4db3c2edbabc5377a708d5d282 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 12:23:54 +0200 Subject: [PATCH 004/113] docs(readme): update to color logo --- README.md | 2 +- logo.jpg | Bin 12200 -> 0 bytes logo.png | Bin 0 -> 16831 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 logo.jpg create mode 100644 logo.png diff --git a/README.md b/README.md index 76ec40db..e35c2f94 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Gin Web Framework - + [![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) [![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin) diff --git a/logo.jpg b/logo.jpg deleted file mode 100644 index bb51852e49aa24ab09ff7826b1f6c3dfdc99b16f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12200 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 Date: Sun, 2 Jul 2017 12:27:38 +0200 Subject: [PATCH 005/113] docs(readme): make logo smaller as before --- logo.png | Bin 16831 -> 21516 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/logo.png b/logo.png index c58c79864852b50763c2aab9106dd9ebd2290cf9..5f4888c4ba851dbfbae9dd1eb77da5e153b39048 100644 GIT binary patch literal 21516 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 DV^9bK literal 16831 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 Date: Sun, 2 Jul 2017 12:29:50 +0200 Subject: [PATCH 006/113] docs(readme): compress logo size --- logo.png | Bin 21516 -> 7987 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/logo.png b/logo.png index 5f4888c4ba851dbfbae9dd1eb77da5e153b39048..20b4fd6b5615c2d39231e7ad991abd5ce67ebcc8 100644 GIT binary patch literal 7987 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 literal 21516 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 DV^9bK From 5ce686113b29d5b275bcf3a46d5896a26ec23595 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 12:46:36 +0200 Subject: [PATCH 007/113] docs(readme): add example on using vendor tool for version lock --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index e35c2f94..494cc8bf 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,38 @@ import "github.com/gin-gonic/gin" import "net/http" ``` +### Use a vendor tool like [Govendor](https://github.com/kardianos/govendor) + +1. `go get` govendor + +```sh +$ go get github.com/kardianos/govendor +``` +2. Create your project folder and `cd` inside + +```sh +$ mkdir -p ~/go/src/github.com/myusername/project && cd "$_" +``` + +3. Vendor init your project and add gin + +```sh +$ govendor init +$ govendor add github.com/gin-gonic/gin@v1.2 +``` + +4. Copy a starting template inside your project + +```sh +$ cp ~/go/src/github.com/gin-gonic/gin/examples/basic/* . +``` + +5. Run your project + +```sh +$ go run main.go +``` + ## API Examples ### Using GET, POST, PUT, PATCH, DELETE and OPTIONS From 84fc31f9f8e18e44bf0be5aae6ed64b50a60a373 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 14:35:10 +0200 Subject: [PATCH 008/113] docs(readme): move contribution guide to a separate file --- CONTRIBUTING.md | 12 ++++++++++++ README.md | 13 ------------- 2 files changed, 12 insertions(+), 13 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..eaa55a32 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,12 @@ +## Contributing + +- With issues: + - Use the search tool before opening a new issue. + - Please provide source code and commit sha if you found a bug. + - Review existing issues and provide feedback or react to them. +- With pull requests: + - Open your pull request against master + - Your pull request should have no more than two commits, if not you should squash them. + - It should pass all tests in the available continuous integrations systems such as TravisCI. + - You should add/modify tests to cover your proposed code changes. + - If your pull request contains a new feature, please document it on the README. \ No newline at end of file diff --git a/README.md b/README.md index 494cc8bf..2200d9d9 100644 --- a/README.md +++ b/README.md @@ -988,19 +988,6 @@ func main() { } ``` -## Contributing - -- With issues: - - Use the search tool before opening a new issue. - - Please provide source code and commit sha if you found a bug. - - Review existing issues and provide feedback or react to them. -- With pull requests: - - Open your pull request against master - - Your pull request should have no more than two commits, if not you should squash them. - - It should pass all tests in the available continuous integrations systems such as TravisCI. - - You should add/modify tests to cover your proposed code changes. - - If your pull request contains a new feature, please document it on the README. - ## Users Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. From 912a7df572192e140e27854a57b1ad2cf80d3037 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 16:28:37 +0200 Subject: [PATCH 009/113] docs(readme): fix step in vendor tool example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2200d9d9..8275383d 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ $ govendor add github.com/gin-gonic/gin@v1.2 4. Copy a starting template inside your project ```sh -$ cp ~/go/src/github.com/gin-gonic/gin/examples/basic/* . +$ curl https://raw.githubusercontent.com/gin-gonic/gin/master/examples/basic/main.go > main.go ``` 5. Run your project From de1fdfd1e54eb09f33b3a62a1b3ad6a955847351 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 16:34:52 +0200 Subject: [PATCH 010/113] docs(readme): fix embedmd build --- CONTRIBUTING.md | 3 ++- README.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eaa55a32..4583205e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,9 +4,10 @@ - Use the search tool before opening a new issue. - Please provide source code and commit sha if you found a bug. - Review existing issues and provide feedback or react to them. + - With pull requests: - Open your pull request against master - Your pull request should have no more than two commits, if not you should squash them. - It should pass all tests in the available continuous integrations systems such as TravisCI. - You should add/modify tests to cover your proposed code changes. - - If your pull request contains a new feature, please document it on the README. \ No newline at end of file + - If your pull request contains a new feature, please document it on the README. diff --git a/README.md b/README.md index 8275383d..8b7d9b22 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ $ mkdir -p ~/go/src/github.com/myusername/project && cd "$_" ```sh $ govendor init -$ govendor add github.com/gin-gonic/gin@v1.2 +$ govendor fetch github.com/gin-gonic/gin@v1.2 ``` 4. Copy a starting template inside your project From 1923b3598377f53e0dd2e108e7f52761e3a69692 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 2 Jul 2017 09:41:06 -0500 Subject: [PATCH 011/113] update document format. (#964) Signed-off-by: Bo-Yi Wu --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4583205e..547b777a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ - Review existing issues and provide feedback or react to them. - With pull requests: - - Open your pull request against master + - Open your pull request against `master` - Your pull request should have no more than two commits, if not you should squash them. - It should pass all tests in the available continuous integrations systems such as TravisCI. - You should add/modify tests to cover your proposed code changes. From 89f0acc2f853be6e5af92a247e87a6d66613b819 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 16:54:49 +0200 Subject: [PATCH 012/113] docs(auth): add missing logs for auth.go --- auth.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/auth.go b/auth.go index 125e659f..5f6d68e9 100644 --- a/auth.go +++ b/auth.go @@ -10,9 +10,11 @@ import ( "strconv" ) +// AuthUserKey is the cookie name for user credential in basic auth const AuthUserKey = "user" type ( + // Accounts defines a key/value for user/pass list of authorized logins Accounts map[string]string authPair struct { Value string From 114b71868ab46a299c1d9daa8aa20e2f5e7018b6 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 16:59:21 +0200 Subject: [PATCH 013/113] refactor(deprecated): remove getcookie --- deprecated.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/deprecated.go b/deprecated.go index 27e8f558..7b50dc70 100644 --- a/deprecated.go +++ b/deprecated.go @@ -5,14 +5,10 @@ package gin import ( - "github.com/gin-gonic/gin/binding" "log" -) -func (c *Context) GetCookie(name string) (string, error) { - log.Println("GetCookie() method is deprecated. Use Cookie() instead.") - return c.Cookie(name) -} + "github.com/gin-gonic/gin/binding" +) // BindWith binds the passed struct pointer using the specified binding engine. // See the binding package. From 484acfc30396fa215376f9c2454d1bdf14c86c29 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 17:05:21 +0200 Subject: [PATCH 014/113] docs(logger): add missing inline docs --- logger.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/logger.go b/logger.go index dc6f1415..a03cde6f 100644 --- a/logger.go +++ b/logger.go @@ -25,14 +25,17 @@ var ( disableColor = false ) +// DisableConsoleColor disables color output in the console func DisableConsoleColor() { disableColor = true } +// ErrorLogger returns a handlerfunc for any error type func ErrorLogger() HandlerFunc { return ErrorLoggerT(ErrorTypeAny) } +// ErrorLoggerT returns a handlerfunc for a given error type func ErrorLoggerT(typ ErrorType) HandlerFunc { return func(c *Context) { c.Next() From 92ddc7d2405b441d5fc9b64e938628dec1382759 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 2 Jul 2017 17:06:53 +0200 Subject: [PATCH 015/113] docs(test): add missing inline docs --- test_helpers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test_helpers.go b/test_helpers.go index e7dd55f7..2aed37f2 100644 --- a/test_helpers.go +++ b/test_helpers.go @@ -8,6 +8,7 @@ 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() From f0b54a30238973ddb0bc9c9a58d8b7af038139b1 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 3 Jul 2017 00:55:21 -0500 Subject: [PATCH 016/113] fix: update autotls example. (#965) --- README.md | 2 +- examples/auto-tls/example2.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8b7d9b22..de38839c 100644 --- a/README.md +++ b/README.md @@ -910,7 +910,7 @@ func main() { Cache: autocert.DirCache("/var/www/.cache"), } - log.Fatal(autotls.RunWithManager(r, m)) + log.Fatal(autotls.RunWithManager(r, &m)) } ``` diff --git a/examples/auto-tls/example2.go b/examples/auto-tls/example2.go index ab8b81e7..01718689 100644 --- a/examples/auto-tls/example2.go +++ b/examples/auto-tls/example2.go @@ -22,5 +22,5 @@ func main() { Cache: autocert.DirCache("/var/www/.cache"), } - log.Fatal(autotls.RunWithManager(r, m)) + log.Fatal(autotls.RunWithManager(r, &m)) } From df67c644795eaa050b378df62129eb08bd98d0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 4 Jul 2017 10:08:41 +0800 Subject: [PATCH 017/113] fix cleanPath spell (#969) --- path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path.go b/path.go index d7e7458b..e3424b13 100644 --- a/path.go +++ b/path.go @@ -5,7 +5,7 @@ package gin -// CleanPath is the URL version of path.Clean, it returns a canonical URL path +// cleanPath is the URL version of path.Clean, it returns a canonical URL path // for p, eliminating . and .. elements. // // The following rules are applied iteratively until no further processing can From baf216e1675df21daf66e4b9e904e13116f09a35 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 4 Jul 2017 03:12:29 -0500 Subject: [PATCH 018/113] add error check. (#972) Signed-off-by: Bo-Yi Wu --- examples/upload-file/multiple/main.go | 18 +++++++++++++++--- examples/upload-file/single/main.go | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/examples/upload-file/multiple/main.go b/examples/upload-file/multiple/main.go index 2f4e9b52..22588348 100644 --- a/examples/upload-file/multiple/main.go +++ b/examples/upload-file/multiple/main.go @@ -17,16 +17,28 @@ func main() { email := c.PostForm("email") // Multipart form - form, _ := c.MultipartForm() + form, err := c.MultipartForm() + if err != nil { + c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error())) + return + } files := form.File["files"] for _, file := range files { // Source - src, _ := file.Open() + src, err := file.Open() + if err != nil { + c.String(http.StatusBadRequest, fmt.Sprintf("file open err: %s", err.Error())) + return + } defer src.Close() // Destination - dst, _ := os.Create(file.Filename) + dst, err := os.Create(file.Filename) + if err != nil { + c.String(http.StatusBadRequest, fmt.Sprintf("Create file err: %s", err.Error())) + return + } defer dst.Close() // Copy diff --git a/examples/upload-file/single/main.go b/examples/upload-file/single/main.go index 9acf5009..1e9596cb 100644 --- a/examples/upload-file/single/main.go +++ b/examples/upload-file/single/main.go @@ -17,12 +17,24 @@ func main() { email := c.PostForm("email") // Source - file, _ := c.FormFile("file") - src, _ := file.Open() + file, err := c.FormFile("file") + if err != nil { + c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error())) + return + } + src, err := file.Open() + if err != nil { + c.String(http.StatusBadRequest, fmt.Sprintf("file open err: %s", err.Error())) + return + } defer src.Close() // Destination - dst, _ := os.Create(file.Filename) + dst, err := os.Create(file.Filename) + if err != nil { + c.String(http.StatusBadRequest, fmt.Sprintf("Create file err: %s", err.Error())) + return + } defer dst.Close() // Copy From ecf1b37850461f680d48ba1cd1351e79b26f742a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 4 Jul 2017 16:17:54 +0800 Subject: [PATCH 019/113] add doc.go (#970) --- doc.go | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 doc.go diff --git a/doc.go b/doc.go new file mode 100644 index 00000000..01ac4a90 --- /dev/null +++ b/doc.go @@ -0,0 +1,6 @@ +/* +Package gin implements a HTTP web framework called gin. + +See https://gin-gonic.github.io/gin/ for more information about gin. +*/ +package gin // import "github.com/gin-gonic/gin" From 735748b359cee5e7f7597128580f6f94bbc0987f Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Tue, 4 Jul 2017 16:36:12 +0800 Subject: [PATCH 020/113] fix(tests): TestDebugPrintLoadTemplate use assert.Regep instead of assert.Equal (#973) --- debug_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_test.go b/debug_test.go index c30081c5..2b010858 100644 --- a/debug_test.go +++ b/debug_test.go @@ -74,7 +74,7 @@ func TestDebugPrintLoadTemplate(t *testing.T) { templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./fixtures/basic/hello.tmpl")) debugPrintLoadTemplate(templ) - assert.Equal(t, w.String(), "[GIN-debug] Loaded HTML Templates (2): \n\t- \n\t- hello.tmpl\n\n") + assert.Regexp(t, `^\[GIN-debug\] Loaded HTML Templates \(2\): \n(\t- \n|\t- hello\.tmpl\n){2}\n`, w.String()) } func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) { From 7d043cedb1ba33756ae9b69fa554a3062bff70d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Wed, 5 Jul 2017 09:55:50 +0800 Subject: [PATCH 021/113] improved swap (#974) --- tree.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tree.go b/tree.go index a39f43bf..55eab99e 100644 --- a/tree.go +++ b/tree.go @@ -105,9 +105,7 @@ func (n *node) incrementChildPrio(pos int) int { newPos := pos for newPos > 0 && n.children[newPos-1].priority < prio { // swap node positions - tmpN := n.children[newPos-1] - n.children[newPos-1] = n.children[newPos] - n.children[newPos] = tmpN + n.children[newPos-1], n.children[newPos] = n.children[newPos], n.children[newPos-1] newPos-- } From 22fc0284e3ae2101f44e8188dde8cad797ce51e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Wed, 5 Jul 2017 10:42:56 +0800 Subject: [PATCH 022/113] update readme (#976) --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index de38839c..80158c8f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi ![Gin console logger](https://gin-gonic.github.io/gin/other/console.png) ```sh -$ cat test.go +# assume the following codes in example.go file +$ cat example.go ``` ```go @@ -32,6 +33,11 @@ func main() { } ``` +``` +# run example.go and visit 0.0.0.0:8080/ping on browser +$ go run example.go +``` + ## Benchmarks Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter) From d535fcd59842094ea88307eafc48bc163c6a2f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Wed, 5 Jul 2017 15:47:36 +0800 Subject: [PATCH 023/113] separate type define (#975) --- auth.go | 18 ++++---- errors.go | 14 +++--- fs.go | 15 +++---- gin.go | 109 ++++++++++++++++++++++----------------------- response_writer.go | 46 +++++++++---------- routergroup.go | 56 +++++++++++------------ 6 files changed, 125 insertions(+), 133 deletions(-) diff --git a/auth.go b/auth.go index 5f6d68e9..410fe5cc 100644 --- a/auth.go +++ b/auth.go @@ -13,15 +13,15 @@ import ( // AuthUserKey is the cookie name for user credential in basic auth const AuthUserKey = "user" -type ( - // Accounts defines a key/value for user/pass list of authorized logins - Accounts map[string]string - authPair struct { - Value string - User string - } - authPairs []authPair -) +// Accounts defines a key/value for user/pass list of authorized logins +type Accounts map[string]string + +type authPair struct { + Value string + User string +} + +type authPairs []authPair func (a authPairs) searchCredential(authValue string) (string, bool) { if len(authValue) == 0 { diff --git a/errors.go b/errors.go index 896af6fc..599d458a 100644 --- a/errors.go +++ b/errors.go @@ -23,15 +23,13 @@ const ( ErrorTypeNu = 2 ) -type ( - Error struct { - Err error - Type ErrorType - Meta interface{} - } +type Error struct { + Err error + Type ErrorType + Meta interface{} +} - errorMsgs []*Error -) +type errorMsgs []*Error var _ error = &Error{} diff --git a/fs.go b/fs.go index 12645826..c1693dd5 100644 --- a/fs.go +++ b/fs.go @@ -9,14 +9,13 @@ import ( "os" ) -type ( - onlyfilesFS struct { - fs http.FileSystem - } - neuteredReaddirFile struct { - http.File - } -) +type onlyfilesFS struct { + fs http.FileSystem +} + +type neuteredReaddirFile struct { + http.File +} // Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally // in router.Static(). diff --git a/gin.go b/gin.go index c4118a4e..e7b1fc15 100644 --- a/gin.go +++ b/gin.go @@ -33,67 +33,66 @@ func (c HandlersChain) Last() HandlerFunc { return nil } -type ( - RoutesInfo []RouteInfo - RouteInfo struct { - Method string - Path string - Handler string - } +type RouteInfo struct { + Method string + Path string + Handler string +} - // Engine is the framework's instance, it contains the muxer, middleware and configuration settings. - // Create an instance of Engine, by using New() or Default() - Engine struct { - RouterGroup - delims render.Delims - HTMLRender render.HTMLRender - FuncMap template.FuncMap - allNoRoute HandlersChain - allNoMethod HandlersChain - noRoute HandlersChain - noMethod HandlersChain - pool sync.Pool - trees methodTrees +type RoutesInfo []RouteInfo - // 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 +// Engine is the framework's instance, it contains the muxer, middleware and configuration settings. +// Create an instance of Engine, by using New() or Default() +type Engine struct { + RouterGroup + delims render.Delims + HTMLRender render.HTMLRender + FuncMap template.FuncMap + allNoRoute HandlersChain + allNoMethod HandlersChain + noRoute HandlersChain + noMethod HandlersChain + pool sync.Pool + trees methodTrees - // 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. - // If a handle can be found for this route, the router makes a redirection - // to the corrected path with status code 301 for GET requests and 307 for - // all other request methods. - // For example /FOO and /..//Foo could be redirected to /foo. - // RedirectTrailingSlash is independent of this option. - RedirectFixedPath bool + // 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 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. - // If no other Method is allowed, the request is delegated to the NotFound - // handler. - HandleMethodNotAllowed bool - ForwardedByClientIP bool + // 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. + // If a handle can be found for this route, the router makes a redirection + // to the corrected path with status code 301 for GET requests and 307 for + // all other request methods. + // For example /FOO and /..//Foo could be redirected to /foo. + // RedirectTrailingSlash is independent of this option. + RedirectFixedPath bool - // #726 #755 If enabled, it will thrust some headers starting with - // 'X-AppEngine...' for better integration with that PaaS. - AppEngine bool + // 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. + // If no other Method is allowed, the request is delegated to the NotFound + // handler. + HandleMethodNotAllowed bool + ForwardedByClientIP bool - // If enabled, the url.RawPath will be used to find parameters. - UseRawPath bool - // If true, the path value will be unescaped. - // If UseRawPath is false (by default), the UnescapePathValues effectively is true, - // as url.Path gonna be used, which is already unescaped. - UnescapePathValues bool - } -) + // #726 #755 If enabled, it will thrust some headers starting with + // 'X-AppEngine...' for better integration with that PaaS. + AppEngine bool + + // If enabled, the url.RawPath will be used to find parameters. + UseRawPath bool + // If true, the path value will be unescaped. + // If UseRawPath is false (by default), the UnescapePathValues effectively is true, + // as url.Path gonna be used, which is already unescaped. + UnescapePathValues bool +} var _ IRouter = &Engine{} diff --git a/response_writer.go b/response_writer.go index fcbe230d..c9d07c33 100644 --- a/response_writer.go +++ b/response_writer.go @@ -16,36 +16,34 @@ const ( defaultStatus = 200 ) -type ( - ResponseWriter interface { - http.ResponseWriter - http.Hijacker - http.Flusher - http.CloseNotifier +type ResponseWriter interface { + http.ResponseWriter + http.Hijacker + http.Flusher + http.CloseNotifier - // Returns the HTTP response status code of the current request. - Status() int + // Returns the HTTP response status code of the current request. + Status() int - // Returns the number of bytes already written into the response http body. - // See Written() - Size() int + // Returns the number of bytes already written into the response http body. + // See Written() + Size() int - // Writes the string into the response body. - WriteString(string) (int, error) + // Writes the string into the response body. + WriteString(string) (int, error) - // Returns true if the response body was already written. - Written() bool + // 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() +} - responseWriter struct { - http.ResponseWriter - size int - status int - } -) +type responseWriter struct { + http.ResponseWriter + size int + status int +} var _ ResponseWriter = &responseWriter{} diff --git a/routergroup.go b/routergroup.go index f22729bb..89ec89aa 100644 --- a/routergroup.go +++ b/routergroup.go @@ -11,39 +11,37 @@ import ( "strings" ) -type ( - IRouter interface { - IRoutes - Group(string, ...HandlerFunc) *RouterGroup - } +type IRouter interface { + IRoutes + Group(string, ...HandlerFunc) *RouterGroup +} - IRoutes interface { - Use(...HandlerFunc) IRoutes +type IRoutes interface { + Use(...HandlerFunc) IRoutes - Handle(string, string, ...HandlerFunc) IRoutes - Any(string, ...HandlerFunc) IRoutes - GET(string, ...HandlerFunc) IRoutes - POST(string, ...HandlerFunc) IRoutes - DELETE(string, ...HandlerFunc) IRoutes - PATCH(string, ...HandlerFunc) IRoutes - PUT(string, ...HandlerFunc) IRoutes - OPTIONS(string, ...HandlerFunc) IRoutes - HEAD(string, ...HandlerFunc) IRoutes + Handle(string, string, ...HandlerFunc) IRoutes + Any(string, ...HandlerFunc) IRoutes + GET(string, ...HandlerFunc) IRoutes + POST(string, ...HandlerFunc) IRoutes + DELETE(string, ...HandlerFunc) IRoutes + PATCH(string, ...HandlerFunc) IRoutes + PUT(string, ...HandlerFunc) IRoutes + OPTIONS(string, ...HandlerFunc) IRoutes + HEAD(string, ...HandlerFunc) IRoutes - StaticFile(string, string) IRoutes - Static(string, string) IRoutes - StaticFS(string, http.FileSystem) IRoutes - } + StaticFile(string, string) IRoutes + Static(string, string) IRoutes + StaticFS(string, http.FileSystem) IRoutes +} - // RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix - // and an array of handlers (middleware) - RouterGroup struct { - Handlers HandlersChain - basePath string - engine *Engine - root bool - } -) +// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix +// and an array of handlers (middleware) +type RouterGroup struct { + Handlers HandlersChain + basePath string + engine *Engine + root bool +} var _ IRouter = &RouterGroup{} From 31ac11a29891bf2d7c2da20054507f3fb5350a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Wed, 5 Jul 2017 17:37:24 +0800 Subject: [PATCH 024/113] update comment method (#977) --- auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth.go b/auth.go index 410fe5cc..7ebf5576 100644 --- a/auth.go +++ b/auth.go @@ -89,6 +89,6 @@ func secureCompare(given, actual string) bool { if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 { return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1 } - /* Securely compare actual to itself to keep constant time, but always return false */ + // Securely compare actual to itself to keep constant time, but always return false return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false } From 2a36eefcffe31ad16d9b27fdc64c083e246e80f0 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Wed, 5 Jul 2017 20:37:28 +0800 Subject: [PATCH 025/113] add test case of mode --- mode_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mode_test.go b/mode_test.go index 2a23d85e..bf6a5a1a 100644 --- a/mode_test.go +++ b/mode_test.go @@ -5,6 +5,7 @@ package gin import ( + "os" "testing" "github.com/stretchr/testify/assert" @@ -28,4 +29,9 @@ func TestSetMode(t *testing.T) { assert.Equal(t, Mode(), TestMode) assert.Panics(t, func() { SetMode("unknown") }) + + os.Setenv(ENV_GIN_MODE, DebugMode) + assert.Equal(t, ginMode, debugCode) + assert.Equal(t, Mode(), DebugMode) + os.Unsetenv(ENV_GIN_MODE) } From f843c333702ca0c52b6170b34b5b514f7e530521 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Wed, 5 Jul 2017 21:05:02 +0800 Subject: [PATCH 026/113] add test case of mode --- mode_test.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mode_test.go b/mode_test.go index bf6a5a1a..0a9cbc3c 100644 --- a/mode_test.go +++ b/mode_test.go @@ -12,10 +12,14 @@ import ( ) func init() { - SetMode(TestMode) + os.Setenv(ENV_GIN_MODE, TestMode) } func TestSetMode(t *testing.T) { + assert.Equal(t, ginMode, testCode) + assert.Equal(t, Mode(), TestMode) + os.Unsetenv(ENV_GIN_MODE) + SetMode(DebugMode) assert.Equal(t, ginMode, debugCode) assert.Equal(t, Mode(), DebugMode) @@ -29,9 +33,4 @@ func TestSetMode(t *testing.T) { assert.Equal(t, Mode(), TestMode) assert.Panics(t, func() { SetMode("unknown") }) - - os.Setenv(ENV_GIN_MODE, DebugMode) - assert.Equal(t, ginMode, debugCode) - assert.Equal(t, Mode(), DebugMode) - os.Unsetenv(ENV_GIN_MODE) } From ee7b912a242e84896be96b3d921354a9b6cc6950 Mon Sep 17 00:00:00 2001 From: Regner Blok-Andersen Date: Wed, 5 Jul 2017 07:17:03 -0700 Subject: [PATCH 027/113] Updating IsDebugging docs to remove redudent words. (#868) --- debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug.go b/debug.go index a121591a..b31ca685 100644 --- a/debug.go +++ b/debug.go @@ -15,7 +15,7 @@ func init() { } // IsDebugging returns true if the framework is running in debug mode. -// Use SetMode(gin.Release) to switch to disable the debug mode. +// Use SetMode(gin.Release) to disable debug mode. func IsDebugging() bool { return ginMode == debugCode } From e4fd80c627c9a63247373a5818f0628cb5ccc103 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Wed, 5 Jul 2017 16:22:58 +0200 Subject: [PATCH 028/113] docs(readme): add sections to template docs --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 80158c8f..ecba6662 100644 --- a/README.md +++ b/README.md @@ -645,6 +645,8 @@ templates/users/index.tmpl {{ end }} ``` +#### Custom Template renderer + You can also use your own html template render ```go @@ -658,6 +660,8 @@ func main() { } ``` +#### Custom Delimiters + You may use custom delims ```go @@ -666,7 +670,7 @@ You may use custom delims r.LoadHTMLGlob("/path/to/templates")) ``` -#### Add custom template funcs +#### Custom Template Funcs main.go From 71d3ae4f922f4d674bc4760b9b9eb290b645fb55 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Wed, 5 Jul 2017 22:50:33 +0800 Subject: [PATCH 029/113] fix(comment): remove todo --- gin.go | 1 - 1 file changed, 1 deletion(-) diff --git a/gin.go b/gin.go index e7b1fc15..92a41449 100644 --- a/gin.go +++ b/gin.go @@ -345,7 +345,6 @@ func (engine *Engine) handleHTTPRequest(context *Context) { } } - // TODO: unit test if engine.HandleMethodNotAllowed { for _, tree := range engine.trees { if tree.method != httpMethod { From 42a34cdc35f72b6b146f48242c52a875bf698dcb Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Wed, 5 Jul 2017 16:55:59 +0200 Subject: [PATCH 030/113] docs(context): document keys, errors, and accepted, close #488 --- context.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index 5196bb13..d8c9af08 100644 --- a/context.go +++ b/context.go @@ -48,9 +48,15 @@ type Context struct { handlers HandlersChain index int8 - engine *Engine - Keys map[string]interface{} - Errors errorMsgs + engine *Engine + + // Keys is a key/value pair exclusively for the context of each request + Keys map[string]interface{} + + // Errors is a list of errors attached to all the handlers/middlewares who used this context + Errors errorMsgs + + // Accepted defines a list of manually accepted formats for content negotiation Accepted []string } From 22ee916dfecdaabe7b541b7f76bdc8c64e99c145 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Wed, 5 Jul 2017 18:25:37 +0200 Subject: [PATCH 031/113] docs(conduct): add code of conduct --- CODE_OF_CONDUCT.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..4ea14f39 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at teamgingonic@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ From b985857899c60f029f73331b81ce281a25724f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Thu, 6 Jul 2017 09:28:16 +0800 Subject: [PATCH 032/113] update func comment (#981) --- context.go | 2 +- errors.go | 11 +++++------ fs.go | 4 ++-- gin.go | 4 ++-- response_writer.go | 6 +++--- tree.go | 4 ++-- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/context.go b/context.go index d8c9af08..2134b8fe 100644 --- a/context.go +++ b/context.go @@ -151,7 +151,7 @@ func (c *Context) AbortWithError(code int, err error) *Error { /********* ERROR MANAGEMENT *********/ /************************************/ -// Attaches an error to the current context. The error is pushed to a list of errors. +// Error attaches an error to the current context. The error is pushed to a list of errors. // It's a good idea to call Error for each error that occurred during the resolution of a request. // A middleware can be used to collect all the errors // and push them to a database together, print a log, or append it in the HTTP response. diff --git a/errors.go b/errors.go index 599d458a..f38d3ff1 100644 --- a/errors.go +++ b/errors.go @@ -69,7 +69,7 @@ func (msg *Error) MarshalJSON() ([]byte, error) { return json.Marshal(msg.JSON()) } -// Implements the error interface +// Error implements the error interface func (msg Error) Error() string { return msg.Err.Error() } @@ -78,7 +78,7 @@ func (msg *Error) IsType(flags ErrorType) bool { return (msg.Type & flags) > 0 } -// Returns a readonly copy filtered the byte. +// ByType returns a readonly copy filtered the byte. // ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic func (a errorMsgs) ByType(typ ErrorType) errorMsgs { if len(a) == 0 { @@ -96,17 +96,16 @@ func (a errorMsgs) ByType(typ ErrorType) errorMsgs { return result } -// Returns the last error in the slice. It returns nil if the array is empty. +// Last returns the last error in the slice. It returns nil if the array is empty. // Shortcut for errors[len(errors)-1] func (a errorMsgs) Last() *Error { - length := len(a) - if length > 0 { + if length := len(a); length > 0 { return a[length-1] } return nil } -// Returns an array will all the error messages. +// Errors returns an array will all the error messages. // Example: // c.Error(errors.New("first")) // c.Error(errors.New("second")) diff --git a/fs.go b/fs.go index c1693dd5..8570a9a9 100644 --- a/fs.go +++ b/fs.go @@ -29,7 +29,7 @@ func Dir(root string, listDirectory bool) http.FileSystem { return &onlyfilesFS{fs} } -// Conforms to http.Filesystem +// Open conforms to http.Filesystem func (fs onlyfilesFS) Open(name string) (http.File, error) { f, err := fs.fs.Open(name) if err != nil { @@ -38,7 +38,7 @@ func (fs onlyfilesFS) Open(name string) (http.File, error) { return neuteredReaddirFile{f}, nil } -// Overrides the http.File default implementation +// Readdir overrides the http.File default implementation func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) { // this disables directory listing return nil, nil diff --git a/gin.go b/gin.go index 92a41449..ca8d75e9 100644 --- a/gin.go +++ b/gin.go @@ -285,7 +285,7 @@ func (engine *Engine) RunUnix(file string) (err error) { return } -// Conforms to the http.Handler interface. +// ServeHTTP conforms to the http.Handler interface. func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { c := engine.pool.Get().(*Context) c.writermem.reset(w) @@ -297,7 +297,7 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { engine.pool.Put(c) } -// Re-enter a context that has been rewritten. +// HandleContext re-enter a context that has been rewritten. // This can be done by setting c.Request.Path to your new target. // Disclaimer: You can loop yourself to death with this, use wisely. func (engine *Engine) HandleContext(c *Context) { diff --git a/response_writer.go b/response_writer.go index c9d07c33..216165b9 100644 --- a/response_writer.go +++ b/response_writer.go @@ -95,7 +95,7 @@ func (w *responseWriter) Written() bool { return w.size != noWritten } -// Implements the http.Hijacker interface +// Hijack implements the http.Hijacker interface func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { if w.size < 0 { w.size = 0 @@ -103,12 +103,12 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { return w.ResponseWriter.(http.Hijacker).Hijack() } -// Implements the http.CloseNotify interface +// CloseNotify implements the http.CloseNotify interface func (w *responseWriter) CloseNotify() <-chan bool { return w.ResponseWriter.(http.CloseNotifier).CloseNotify() } -// Implements the http.Flush interface +// Flush implements the http.Flush interface func (w *responseWriter) Flush() { w.ResponseWriter.(http.Flusher).Flush() } diff --git a/tree.go b/tree.go index 55eab99e..750ffae8 100644 --- a/tree.go +++ b/tree.go @@ -357,7 +357,7 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle n.handlers = handlers } -// Returns the handle registered with the given path (key). The values of +// getValue returns the handle registered with the given path (key). The values of // wildcards are saved to a map. // If no handle can be found, a TSR (trailing slash redirect) recommendation is // made if a handle exists with an extra (without the) trailing slash for the @@ -499,7 +499,7 @@ walk: // Outer loop for walking the tree } } -// Makes a case-insensitive lookup of the given path and tries to find a handler. +// findCaseInsensitivePath makes a case-insensitive lookup of the given path and tries to find a handler. // It can optionally also fix trailing slashes. // It returns the case-corrected path and a bool indicating whether the lookup // was successful. From 436d8e2af9198f5548a73cb96dbeb1cede403a5b Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Thu, 6 Jul 2017 09:30:09 +0800 Subject: [PATCH 033/113] Improve serveError code coverage (#980) --- routes_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/routes_test.go b/routes_test.go index 7464d5d0..41693eed 100644 --- a/routes_test.go +++ b/routes_test.go @@ -439,3 +439,15 @@ func TestRouteRawPathNoUnescape(t *testing.T) { w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/333") assert.Equal(t, w.Code, 200) } + +func TestRouteServeErrorWithWriteHeader(t *testing.T) { + route := New() + route.Use(func(c *Context) { + c.Status(421) + c.Next() + }) + + w := performRequest(route, "GET", "/NotFound") + assert.Equal(t, 421, w.Code) + assert.Equal(t, 0, w.Body.Len()) +} From ff5788bfdd513e659ee15db712a65370db4eb6ab Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Thu, 6 Jul 2017 08:41:27 +0200 Subject: [PATCH 034/113] docs(readme): upload full size logo --- README.md | 2 +- logo.png | Bin 7987 -> 87064 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ecba6662..191546fa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Gin Web Framework - + [![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) [![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin) diff --git a/logo.png b/logo.png index 20b4fd6b5615c2d39231e7ad991abd5ce67ebcc8..7942ad563f911cf8e828e140c549e1f7f7c34b8c 100644 GIT binary patch literal 87064 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Œ`g=nh63pw9&cVdn#VbV}KUEd8jo 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

(|EKdECeR9bkl+}&4+ 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 literal 7987 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 From 7387ef60bec9d9d68e36127896075391431bb343 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Thu, 6 Jul 2017 08:53:48 +0200 Subject: [PATCH 035/113] docs(readme): add badge for projects who use gin --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 191546fa..fcf3b8cf 100644 --- a/README.md +++ b/README.md @@ -998,7 +998,7 @@ func main() { } ``` -## Users +## Users [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge) Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. From 34e972e15545847386a54f9268141cd3a789170a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Thu, 6 Jul 2017 20:59:27 +0800 Subject: [PATCH 036/113] Separate type define (#983) --- render/html.go | 46 ++++++++++++++++++++++------------------------ render/json.go | 14 ++++++-------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/render/html.go b/render/html.go index cf91219c..332d3ba2 100644 --- a/render/html.go +++ b/render/html.go @@ -9,34 +9,32 @@ import ( "net/http" ) -type ( - Delims struct { - Left string - Right string - } +type Delims struct { + Left string + Right string +} - HTMLRender interface { - Instance(string, interface{}) Render - } +type HTMLRender interface { + Instance(string, interface{}) Render +} - HTMLProduction struct { - Template *template.Template - Delims Delims - } +type HTMLProduction struct { + Template *template.Template + Delims Delims +} - HTMLDebug struct { - Files []string - Glob string - Delims Delims - FuncMap template.FuncMap - } +type HTMLDebug struct { + Files []string + Glob string + Delims Delims + FuncMap template.FuncMap +} - HTML struct { - Template *template.Template - Name string - Data interface{} - } -) +type HTML struct { + Template *template.Template + Name string + Data interface{} +} var htmlContentType = []string{"text/html; charset=utf-8"} diff --git a/render/json.go b/render/json.go index 3ee8b132..4e3b66b2 100644 --- a/render/json.go +++ b/render/json.go @@ -9,15 +9,13 @@ import ( "net/http" ) -type ( - JSON struct { - Data interface{} - } +type JSON struct { + Data interface{} +} - IndentedJSON struct { - Data interface{} - } -) +type IndentedJSON struct { + Data interface{} +} var jsonContentType = []string{"application/json; charset=utf-8"} From e0fd6238d3cb237d3eb4c3d13792caa265ecdf29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Thu, 6 Jul 2017 22:49:54 +0800 Subject: [PATCH 037/113] use comma ok and use single line (#984) --- context.go | 10 +--------- gin.go | 3 +-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/context.go b/context.go index 2134b8fe..604dc4ef 100644 --- a/context.go +++ b/context.go @@ -562,15 +562,7 @@ func (c *Context) GetRawData() ([]byte, error) { return ioutil.ReadAll(c.Request.Body) } -func (c *Context) SetCookie( - name string, - value string, - maxAge int, - path string, - domain string, - secure bool, - httpOnly bool, -) { +func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) { if path == "" { path = "/" } diff --git a/gin.go b/gin.go index ca8d75e9..3cac936c 100644 --- a/gin.go +++ b/gin.go @@ -26,8 +26,7 @@ type HandlersChain []HandlerFunc // Last returns the last handler in the chain. ie. the last handler is the main own. func (c HandlersChain) Last() HandlerFunc { - length := len(c) - if length > 0 { + if length := len(c); length > 0 { return c[length-1] } return nil From 7eb943e70aa5d526ad61d6a6ab576ef01bcf29c8 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Fri, 7 Jul 2017 08:43:47 +0800 Subject: [PATCH 038/113] Use single line (#985) --- utils.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/utils.go b/utils.go index 18064fb5..968570c7 100644 --- a/utils.go +++ b/utils.go @@ -100,12 +100,10 @@ func parseAccept(acceptHeader string) []string { parts := strings.Split(acceptHeader, ",") out := make([]string, 0, len(parts)) for _, part := range parts { - index := strings.IndexByte(part, ';') - if index >= 0 { + if index := strings.IndexByte(part, ';'); index >= 0 { part = part[0:index] } - part = strings.TrimSpace(part) - if len(part) > 0 { + if part = strings.TrimSpace(part); len(part) > 0 { out = append(out, part) } } From 0c3726b2061604995defd15bbd673dc4d7065e1c Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Fri, 7 Jul 2017 09:21:44 +0200 Subject: [PATCH 039/113] docs(readme): remove logo, relink it to gin-gonic/logo --- README.md | 2 +- logo.png | Bin 87064 -> 0 bytes 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 logo.png diff --git a/README.md b/README.md index fcf3b8cf..4117769a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Gin Web Framework - + [![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) [![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin) diff --git a/logo.png b/logo.png deleted file mode 100644 index 7942ad563f911cf8e828e140c549e1f7f7c34b8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87064 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Œ`g=nh63pw9&cVdn#VbV}KUEd8jo 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

(|EKdECeR9bkl+}&4+ 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 From 75ed286c6038b2a366ecf033a79d31764eeeb041 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Sat, 8 Jul 2017 01:21:30 +0800 Subject: [PATCH 040/113] feat: add SecureJSON func to prevent json hijacking --- context.go | 7 +++++++ context_test.go | 26 ++++++++++++++++++++++++++ gin.go | 25 ++++++++++++++++--------- render/json.go | 26 ++++++++++++++++++++++++++ render/render.go | 1 + render/render_test.go | 25 +++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 9 deletions(-) diff --git a/context.go b/context.go index 604dc4ef..acf2cd23 100644 --- a/context.go +++ b/context.go @@ -616,6 +616,13 @@ func (c *Context) IndentedJSON(code int, obj interface{}) { 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{}) { + c.Render(code, render.SecureJSON{Prefix: c.engine.secureJsonPrefix, Data: obj}) +} + // 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{}) { diff --git a/context_test.go b/context_test.go index fe31f09a..96e9f08e 100644 --- a/context_test.go +++ b/context_test.go @@ -598,6 +598,32 @@ func TestContextRenderNoContentIndentedJSON(t *testing.T) { assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8") } +// Tests that the response is serialized as Secure JSON +// and Content-Type is set to application/json +func TestContextRenderSecureJSON(t *testing.T) { + w := httptest.NewRecorder() + c, router := CreateTestContext(w) + + router.SecureJsonPrefix("&&&START&&&") + c.SecureJSON(201, []string{"foo", "bar"}) + + assert.Equal(t, w.Code, 201) + assert.Equal(t, w.Body.String(), "&&&START&&&[\"foo\",\"bar\"]") + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8") +} + +// Tests that no Custom JSON is rendered if code is 204 +func TestContextRenderNoContentSecureJSON(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.SecureJSON(204, []string{"foo", "bar"}) + + assert.Equal(t, 204, w.Code) + assert.Equal(t, "", w.Body.String()) + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8") +} + // Tests that the response executes the templates // and responds with Content-Type set to text/html func TestContextRenderHTML(t *testing.T) { diff --git a/gin.go b/gin.go index 3cac936c..8388ac7b 100644 --- a/gin.go +++ b/gin.go @@ -44,15 +44,16 @@ type RoutesInfo []RouteInfo // Create an instance of Engine, by using New() or Default() type Engine struct { RouterGroup - delims render.Delims - HTMLRender render.HTMLRender - FuncMap template.FuncMap - allNoRoute HandlersChain - allNoMethod HandlersChain - noRoute HandlersChain - noMethod HandlersChain - pool sync.Pool - trees methodTrees + delims render.Delims + secureJsonPrefix string + HTMLRender render.HTMLRender + FuncMap template.FuncMap + allNoRoute HandlersChain + allNoMethod HandlersChain + noRoute HandlersChain + noMethod HandlersChain + pool sync.Pool + trees methodTrees // Enables automatic redirection if the current route can't be matched but a // handler for the path with (without) the trailing slash exists. @@ -121,6 +122,7 @@ func New() *Engine { UnescapePathValues: true, trees: make(methodTrees, 0, 9), delims: render.Delims{"{{", "}}"}, + secureJsonPrefix: "while(1);", } engine.RouterGroup.engine = engine engine.pool.New = func() interface{} { @@ -145,6 +147,11 @@ func (engine *Engine) Delims(left, right string) *Engine { return engine } +func (engine *Engine) SecureJsonPrefix(prefix string) *Engine { + engine.secureJsonPrefix = prefix + return engine +} + func (engine *Engine) LoadHTMLGlob(pattern string) { if IsDebugging() { debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern))) diff --git a/render/json.go b/render/json.go index 4e3b66b2..8b64f533 100644 --- a/render/json.go +++ b/render/json.go @@ -5,6 +5,7 @@ package render import ( + "bytes" "encoding/json" "net/http" ) @@ -17,6 +18,13 @@ type IndentedJSON struct { Data interface{} } +type SecureJSON struct { + Prefix string + Data interface{} +} + +type SecureJSONPrefix string + var jsonContentType = []string{"application/json; charset=utf-8"} func (r JSON) Render(w http.ResponseWriter) (err error) { @@ -53,3 +61,21 @@ func (r IndentedJSON) Render(w http.ResponseWriter) error { func (r IndentedJSON) WriteContentType(w http.ResponseWriter) { writeContentType(w, jsonContentType) } + +func (r SecureJSON) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + jsonBytes, err := json.Marshal(r.Data) + if err != nil { + return err + } + // if the jsonBytes is array values + if bytes.HasPrefix(jsonBytes, []byte("[")) && bytes.HasSuffix(jsonBytes, []byte("]")) { + w.Write([]byte(r.Prefix)) + } + w.Write(jsonBytes) + return nil +} + +func (r SecureJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} diff --git a/render/render.go b/render/render.go index 46291421..620b0d87 100644 --- a/render/render.go +++ b/render/render.go @@ -14,6 +14,7 @@ type Render interface { var ( _ Render = JSON{} _ Render = IndentedJSON{} + _ Render = SecureJSON{} _ Render = XML{} _ Render = String{} _ Render = Redirect{} diff --git a/render/render_test.go b/render/render_test.go index c48235c3..e4df5b6d 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -66,6 +66,31 @@ func TestRenderIndentedJSON(t *testing.T) { assert.Equal(t, w.Header().Get("Content-Type"), "application/json; charset=utf-8") } +func TestRenderSecureJSON(t *testing.T) { + w1 := httptest.NewRecorder() + data := map[string]interface{}{ + "foo": "bar", + } + + err1 := (SecureJSON{"while(1);", data}).Render(w1) + + assert.NoError(t, err1) + assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String()) + assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type")) + + w2 := httptest.NewRecorder() + datas := []map[string]interface{}{{ + "foo": "bar", + }, { + "bar": "foo", + }} + + err2 := (SecureJSON{"while(1);", datas}).Render(w2) + assert.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")) +} + type xmlmap map[string]interface{} // Allows type H to be used with xml.Marshal From b060a5f409b8d3d24bf547cc737d688743fd8810 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Sat, 8 Jul 2017 16:16:59 +0800 Subject: [PATCH 041/113] docs(readme): fix code link (#989) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4117769a..eda1dd70 100644 --- a/README.md +++ b/README.md @@ -471,7 +471,7 @@ func startPage(c *gin.Context) { var person Person // If `GET`, only `Form` binding engine (`query`) used. // If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`). - // See more at https://github.com/gin-gonic/gin/blob/develop/binding/binding.go#L45 + // See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48 if c.Bind(&person) == nil { log.Println(person.Name) log.Println(person.Address) From 12508320c2834204677e29be58fec8103facdaf8 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 8 Jul 2017 16:49:09 +0800 Subject: [PATCH 042/113] feat: change json lib to jsoniter A high-performance 100% compatible drop-in replacement of "encoding/json" https://github.com/json-iterator/go Signed-off-by: Bo-Yi Wu --- binding/json.go | 3 ++- errors.go | 3 ++- errors_test.go | 2 +- render/json.go | 3 ++- vendor/vendor.json | 6 ++++++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/binding/json.go b/binding/json.go index 486b9733..aa7039d3 100644 --- a/binding/json.go +++ b/binding/json.go @@ -5,8 +5,9 @@ package binding import ( - "encoding/json" "net/http" + + json "github.com/json-iterator/go" ) type jsonBinding struct{} diff --git a/errors.go b/errors.go index f38d3ff1..19b2cfbe 100644 --- a/errors.go +++ b/errors.go @@ -6,9 +6,10 @@ package gin import ( "bytes" - "encoding/json" "fmt" "reflect" + + json "github.com/json-iterator/go" ) type ErrorType uint64 diff --git a/errors_test.go b/errors_test.go index 1aa0cdde..46ae536e 100644 --- a/errors_test.go +++ b/errors_test.go @@ -5,10 +5,10 @@ package gin import ( - "encoding/json" "errors" "testing" + json "github.com/json-iterator/go" "github.com/stretchr/testify/assert" ) diff --git a/render/json.go b/render/json.go index 8b64f533..38967409 100644 --- a/render/json.go +++ b/render/json.go @@ -6,8 +6,9 @@ package render import ( "bytes" - "encoding/json" "net/http" + + json "github.com/json-iterator/go" ) type JSON struct { diff --git a/vendor/vendor.json b/vendor/vendor.json index e520540b..547bb2e0 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -33,6 +33,12 @@ "revision": "5a0f697c9ed9d68fef0116532c6e05cfeae00e55", "revisionTime": "2017-06-01T23:02:30Z" }, + { + "checksumSHA1": "gWQ2ncPI6qpTwS3e6/ShPwUP1uo=", + "path": "github.com/json-iterator/go", + "revision": "b1afefe0580e6e818dd50da9593f477c80ccd67d", + "revisionTime": "2017-07-07T13:43:33Z" + }, { "checksumSHA1": "9if9IBLsxkarJ804NPWAzgskIAk=", "path": "github.com/manucorporat/stats", From 08338eff821be0a8805aab51ac35eac856e6a3ed Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 8 Jul 2017 17:03:14 +0800 Subject: [PATCH 043/113] fix testing Signed-off-by: Bo-Yi Wu --- context_test.go | 6 +++--- errors_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/context_test.go b/context_test.go index 96e9f08e..6c4af42f 100644 --- a/context_test.go +++ b/context_test.go @@ -582,8 +582,8 @@ func TestContextRenderIndentedJSON(t *testing.T) { c.IndentedJSON(201, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}) assert.Equal(t, w.Code, 201) - assert.Equal(t, w.Body.String(), "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}") - assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8") + assert.Equal(t, "{\n \"foo\":\"bar\",\n \"bar\":\"foo\",\n \"nested\":{\n \"foo\":\"bar\"\n }\n}", w.Body.String()) + assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) } // Tests that no Custom JSON is rendered if code is 204 @@ -595,7 +595,7 @@ func TestContextRenderNoContentIndentedJSON(t *testing.T) { assert.Equal(t, 204, w.Code) assert.Equal(t, "", w.Body.String()) - assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8") + assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) } // Tests that the response is serialized as Secure JSON diff --git a/errors_test.go b/errors_test.go index 46ae536e..85782c07 100644 --- a/errors_test.go +++ b/errors_test.go @@ -91,7 +91,7 @@ Error #03: third H{"error": "third", "status": "400"}, }) jsonBytes, _ := json.Marshal(errs) - assert.Equal(t, string(jsonBytes), "[{\"error\":\"first\"},{\"error\":\"second\",\"meta\":\"some data\"},{\"error\":\"third\",\"status\":\"400\"}]") + assert.Equal(t, "[{\"error\":\"first\"},{\"meta\":\"some data\",\"error\":\"second\"},{\"status\":\"400\",\"error\":\"third\"}]", string(jsonBytes)) errs = errorMsgs{ {Err: errors.New("first"), Type: ErrorTypePrivate}, } From cb524fc94e322efd6b1bf82688af5109cffd06c7 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 8 Jul 2017 17:11:28 +0800 Subject: [PATCH 044/113] fix testing Signed-off-by: Bo-Yi Wu --- errors_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors_test.go b/errors_test.go index 85782c07..778ecf0a 100644 --- a/errors_test.go +++ b/errors_test.go @@ -32,7 +32,7 @@ func TestError(t *testing.T) { }) jsonBytes, _ := json.Marshal(err) - assert.Equal(t, string(jsonBytes), "{\"error\":\"test error\",\"meta\":\"some data\"}") + assert.Equal(t, "{\"meta\":\"some data\",\"error\":\"test error\"}", string(jsonBytes)) err.SetMeta(H{ "status": "200", From e23842ecab161390b6b537c1c906d7e713f07db0 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 8 Jul 2017 18:19:09 +0800 Subject: [PATCH 045/113] fix json sort the map keys Signed-off-by: Bo-Yi Wu --- binding/json.go | 4 +++- context_test.go | 2 +- errors.go | 4 +++- errors_test.go | 5 ++--- render/json.go | 4 +++- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/binding/json.go b/binding/json.go index aa7039d3..20aae8cf 100644 --- a/binding/json.go +++ b/binding/json.go @@ -7,9 +7,11 @@ package binding import ( "net/http" - json "github.com/json-iterator/go" + "github.com/json-iterator/go" ) +var json = jsoniter.ConfigCompatibleWithStandardLibrary + type jsonBinding struct{} func (jsonBinding) Name() string { diff --git a/context_test.go b/context_test.go index 6c4af42f..f57fa6fd 100644 --- a/context_test.go +++ b/context_test.go @@ -582,7 +582,7 @@ func TestContextRenderIndentedJSON(t *testing.T) { c.IndentedJSON(201, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}) assert.Equal(t, w.Code, 201) - assert.Equal(t, "{\n \"foo\":\"bar\",\n \"bar\":\"foo\",\n \"nested\":{\n \"foo\":\"bar\"\n }\n}", w.Body.String()) + 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")) } diff --git a/errors.go b/errors.go index 19b2cfbe..26d4e474 100644 --- a/errors.go +++ b/errors.go @@ -9,9 +9,11 @@ import ( "fmt" "reflect" - json "github.com/json-iterator/go" + "github.com/json-iterator/go" ) +var json = jsoniter.ConfigCompatibleWithStandardLibrary + type ErrorType uint64 const ( diff --git a/errors_test.go b/errors_test.go index 778ecf0a..a0d95504 100644 --- a/errors_test.go +++ b/errors_test.go @@ -8,7 +8,6 @@ import ( "errors" "testing" - json "github.com/json-iterator/go" "github.com/stretchr/testify/assert" ) @@ -32,7 +31,7 @@ func TestError(t *testing.T) { }) jsonBytes, _ := json.Marshal(err) - assert.Equal(t, "{\"meta\":\"some data\",\"error\":\"test error\"}", string(jsonBytes)) + assert.Equal(t, "{\"error\":\"test error\",\"meta\":\"some data\"}", string(jsonBytes)) err.SetMeta(H{ "status": "200", @@ -91,7 +90,7 @@ Error #03: third H{"error": "third", "status": "400"}, }) jsonBytes, _ := json.Marshal(errs) - assert.Equal(t, "[{\"error\":\"first\"},{\"meta\":\"some data\",\"error\":\"second\"},{\"status\":\"400\",\"error\":\"third\"}]", string(jsonBytes)) + assert.Equal(t, "[{\"error\":\"first\"},{\"error\":\"second\",\"meta\":\"some data\"},{\"error\":\"third\",\"status\":\"400\"}]", string(jsonBytes)) errs = errorMsgs{ {Err: errors.New("first"), Type: ErrorTypePrivate}, } diff --git a/render/json.go b/render/json.go index 38967409..17e6ac86 100644 --- a/render/json.go +++ b/render/json.go @@ -8,9 +8,11 @@ import ( "bytes" "net/http" - json "github.com/json-iterator/go" + "github.com/json-iterator/go" ) +var json = jsoniter.ConfigCompatibleWithStandardLibrary + type JSON struct { Data interface{} } From bf9758ca05e47a74d55995ef62042a1c62cbb53c Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Sun, 9 Jul 2017 01:54:43 +0800 Subject: [PATCH 046/113] Add SecureJSON doc --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index eda1dd70..cc97c7a5 100644 --- a/README.md +++ b/README.md @@ -559,6 +559,29 @@ func main() { } ``` +#### 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") +} +``` + ### Serving static files ```go From df3b79e805233f553c0495cfa1fb76766b9d80b3 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Sun, 9 Jul 2017 11:02:44 +0200 Subject: [PATCH 047/113] docs(readme): add binding html checkbox example, close #129 (#994) --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/README.md b/README.md index cc97c7a5..05b696df 100644 --- a/README.md +++ b/README.md @@ -481,6 +481,52 @@ func startPage(c *gin.Context) { } ``` +### 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.Bind(&fakeForm) + c.JSON(200, gin.H{"color": fakeForm.Colors}) +} + +... + +``` + +form.html + +```html +

+

Check some colors

+ + + + + + + +
+``` + +result: + +``` +{"color":["red","green","blue"]} +``` + ### Multipart/Urlencoded binding ```go From 8436a9d829f22f56cbc059e06a68d19163161391 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Mon, 10 Jul 2017 14:45:19 +0800 Subject: [PATCH 048/113] fix(render): remove repeated static check. (#998) --- render/render.go | 1 - 1 file changed, 1 deletion(-) diff --git a/render/render.go b/render/render.go index 620b0d87..71852364 100644 --- a/render/render.go +++ b/render/render.go @@ -24,7 +24,6 @@ var ( _ HTMLRender = HTMLProduction{} _ Render = YAML{} _ Render = MsgPack{} - _ Render = MsgPack{} ) func writeContentType(w http.ResponseWriter, value []string) { From b6256dbe0ce7b14e1b8da3abbc710c01ac56c658 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Mon, 10 Jul 2017 08:57:09 +0200 Subject: [PATCH 049/113] chore(vendor): update jsonite rev, close #991 (#992) --- vendor/vendor.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 547bb2e0..cb752e91 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -34,10 +34,10 @@ "revisionTime": "2017-06-01T23:02:30Z" }, { - "checksumSHA1": "gWQ2ncPI6qpTwS3e6/ShPwUP1uo=", + "checksumSHA1": "YY//OXgGrJOsiR8Cz1xexk/zwkc=", "path": "github.com/json-iterator/go", - "revision": "b1afefe0580e6e818dd50da9593f477c80ccd67d", - "revisionTime": "2017-07-07T13:43:33Z" + "revision": "37ba1b32b5ed52dfaff0fe50d923d210c11c8daf", + "revisionTime": "2017-07-08T17:01:13Z" }, { "checksumSHA1": "9if9IBLsxkarJ804NPWAzgskIAk=", From fb7448f081196b9c86cade4f58b663cab2207025 Mon Sep 17 00:00:00 2001 From: whirosan Date: Mon, 10 Jul 2017 17:33:35 +0900 Subject: [PATCH 050/113] feat(binding): add UseNumber() in gin.Context.BindJSON() (#997) close #368 * resolve #368 add option to UseNumber() in gin.Context.BindJSON() * add test --- binding/json.go | 8 +++++++- mode.go | 4 ++++ mode_test.go | 7 +++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/binding/json.go b/binding/json.go index 20aae8cf..215fa9bf 100644 --- a/binding/json.go +++ b/binding/json.go @@ -10,7 +10,10 @@ import ( "github.com/json-iterator/go" ) -var json = jsoniter.ConfigCompatibleWithStandardLibrary +var ( + json = jsoniter.ConfigCompatibleWithStandardLibrary + EnableDecoderUseNumber = false +) type jsonBinding struct{} @@ -20,6 +23,9 @@ func (jsonBinding) Name() string { func (jsonBinding) Bind(req *http.Request, obj interface{}) error { decoder := json.NewDecoder(req.Body) + if EnableDecoderUseNumber { + decoder.UseNumber() + } if err := decoder.Decode(obj); err != nil { return err } diff --git a/mode.go b/mode.go index e24dbdc2..b0d2c27d 100644 --- a/mode.go +++ b/mode.go @@ -64,6 +64,10 @@ func DisableBindValidation() { binding.Validator = nil } +func EnableJsonDecoderUseNumber() { + binding.EnableDecoderUseNumber = true +} + func Mode() string { return modeName } diff --git a/mode_test.go b/mode_test.go index 0a9cbc3c..f3b88a12 100644 --- a/mode_test.go +++ b/mode_test.go @@ -8,6 +8,7 @@ import ( "os" "testing" + "github.com/gin-gonic/gin/binding" "github.com/stretchr/testify/assert" ) @@ -34,3 +35,9 @@ func TestSetMode(t *testing.T) { assert.Panics(t, func() { SetMode("unknown") }) } + +func TestEnableJsonDecoderUseNumber(t *testing.T) { + assert.False(t, binding.EnableDecoderUseNumber) + EnableJsonDecoderUseNumber() + assert.True(t, binding.EnableDecoderUseNumber) +} From e31cbdf241b1ff161e3bc5eb4af1c9601fbb7639 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 10 Jul 2017 03:41:20 -0500 Subject: [PATCH 051/113] feat(logger): show query string in logger. (#999) close #988 Signed-off-by: Bo-Yi Wu --- logger.go | 5 +++++ logger_test.go | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/logger.go b/logger.go index a03cde6f..470e4bb6 100644 --- a/logger.go +++ b/logger.go @@ -77,6 +77,7 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { // Start timer start := time.Now() path := c.Request.URL.Path + raw := c.Request.URL.RawQuery // Process request c.Next() @@ -97,6 +98,10 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { } comment := c.Errors.ByType(ErrorTypePrivate).String() + if raw != "" { + path = path + "?" + raw + } + fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s", end.Format("2006/01/02 - 15:04:05"), statusColor, statusCode, reset, diff --git a/logger_test.go b/logger_test.go index 15d6ee9c..62c1366f 100644 --- a/logger_test.go +++ b/logger_test.go @@ -28,10 +28,11 @@ func TestLogger(t *testing.T) { router.HEAD("/example", func(c *Context) {}) router.OPTIONS("/example", func(c *Context) {}) - performRequest(router, "GET", "/example") + performRequest(router, "GET", "/example?a=100") assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), "/example") + assert.Contains(t, buffer.String(), "a=100") // I wrote these first (extending the above) but then realized they are more // like integration tests because they test the whole logging process rather From 44da058aa09ea4241c37d8f92b6ac6225a4b1aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Mon, 10 Jul 2017 22:21:42 +0800 Subject: [PATCH 052/113] add favicon example (#1001) --- examples/favicon/favicon.ico | Bin 0 -> 1150 bytes examples/favicon/main.go | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 examples/favicon/favicon.ico create mode 100644 examples/favicon/main.go diff --git a/examples/favicon/favicon.ico b/examples/favicon/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3959cd7f9b13333df2f132f736c5f1d562278895 GIT binary patch literal 1150 zcmZvaZAep57{~u-FK%{oZPV!zqumdgW0lj0f*e`-B@(PiL9v7&C`hOvv*<%AsnCmM z1{IQ-Wf{?n%U;YZA+mQGi`IuSL&;vX7hRjRdwR}RV$i+(&bjCOpZ|03bM6B$x{XHA zXu~=U5Dy@Qpe@>o?9&907Ar*vum3Z+TrJ4TYEn6!ZB~b)J=Nj3J|jKdZO+d>z?Lj& z_>Uv9Ww+Nrr5c6J){<rb+kFDFx&2;ZqPs+vPbDNJxiNe8wtw=N&0AqM zcOowC@WP1`Pd?LV&T_`u9s(V?#9GE$d$rm#++a9y!(ypRwpflKCMItzha??@B<-87 z(;f9PR?mT^uRv=S;09x7Del60;CM)-s^@zB;Z{kiHbqD84*MC~Los%yRyWo#)=b{h z#QD3xRReH|V%h}LfOCC7GksU{PmQKwjaDo2mXP-%)qNq6u}&Y*MP9+}6F@NoTK@R2 zP_C|fe|5$>T2-osD8^2c?j<~Pfu3)`8}jxNo_zpKI7zsP<59r#D-m79ynF#Xumbn# z{X@j3OvZlrfgou=h@NW3g#Qq6m8hUU|CWjodXQ=udBa%0jlhHneqtnDl9WM7;#`tK zJSwVhe?o%ri~4U8^*X)&Xh zx9Du{kiWw?dGcUb7csINYBHJn)mG~eoK8P-ayVQrWot$TR|xKKe11%47^~HG!({SM zZ$F)pmNxuOXf7B3o{^zsFId1abLIf$23D`;g80HheyyN@^XzQDUzU9OoZinxyEuqKAfmpL|X4q(TQVDg3yLC>a53eU?Md^Kmz%hLJtUsn|s zegCv^qr!`egXgQOL#DDaWy~9SFd{Xz2M&iX)o%ZKv$T|#)YK?vcJ2(^FE0&)6%-lPA8Q&?sB<0v3N1ZvSkD1^X3h2@%dik{d=zVczBH0%jvIh>Q6<# zXwkPLxw(DI3kp7ra|274KB09EcI_HTuB;3gtEvM3=#7mZP**pKy?X=J%F5yR+S;FE kdSEK>WfFCrj=Hk~C=--X$BaX)h1R8x#EIB`qMMHYHxij({r~^~ literal 0 HcmV?d00001 diff --git a/examples/favicon/main.go b/examples/favicon/main.go new file mode 100644 index 00000000..5ad39331 --- /dev/null +++ b/examples/favicon/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/gin-gonic/gin" + "github.com/thinkerou/favicon" +) + +func main() { + app := gin.Default() + app.Use(favicon.New("./favicon.ico")) + app.GET("/ping", func(c *gin.Context) { + c.String(200, "Hello favicon.") + }) + app.Run(":8080") +} From 87fdff8f88e22722ce258ebb46e5285341ebe523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 11 Jul 2017 09:03:09 +0800 Subject: [PATCH 053/113] [docs] add http2 example (#1000) --- examples/http2/README.md | 18 +++++++++++++++++ examples/http2/main.go | 32 ++++++++++++++++++++++++++++++ examples/http2/testdata/ca.pem | 15 ++++++++++++++ examples/http2/testdata/server.key | 16 +++++++++++++++ examples/http2/testdata/server.pem | 16 +++++++++++++++ 5 files changed, 97 insertions(+) create mode 100644 examples/http2/README.md create mode 100644 examples/http2/main.go create mode 100644 examples/http2/testdata/ca.pem create mode 100644 examples/http2/testdata/server.key create mode 100644 examples/http2/testdata/server.pem diff --git a/examples/http2/README.md b/examples/http2/README.md new file mode 100644 index 00000000..42dd4b8a --- /dev/null +++ b/examples/http2/README.md @@ -0,0 +1,18 @@ +## How to generate RSA private key and digital certificate + +1. Install Openssl + +Please visit https://github.com/openssl/openssl to get pkg and install. + +2. Generate RSA private key + +```sh +$ mkdir testdata +$ openssl genrsa -out ./testdata/server.key 2048 +``` + +3. Generate digital certificate + +```sh +$ openssl req -new -x509 -key ./testdata/server.key -out ./testdata/server.pem -days 365 +``` diff --git a/examples/http2/main.go b/examples/http2/main.go new file mode 100644 index 00000000..19e65f84 --- /dev/null +++ b/examples/http2/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "html/template" + + "github.com/gin-gonic/gin" +) + +var html = template.Must(template.New("https").Parse(` + + + Https Test + + +

Welcome, Ginner!

+ + +`)) + +func main() { + r := gin.Default() + r.SetHTMLTemplate(html) + + r.GET("/welcome", func(c *gin.Context) { + c.HTML(200, "https", gin.H{ + "status": "success", + }) + }) + + // Listen and Server in https://127.0.0.1:8080 + r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key") +} diff --git a/examples/http2/testdata/ca.pem b/examples/http2/testdata/ca.pem new file mode 100644 index 00000000..6c8511a7 --- /dev/null +++ b/examples/http2/testdata/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/examples/http2/testdata/server.key b/examples/http2/testdata/server.key new file mode 100644 index 00000000..143a5b87 --- /dev/null +++ b/examples/http2/testdata/server.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/examples/http2/testdata/server.pem b/examples/http2/testdata/server.pem new file mode 100644 index 00000000..f3d43fcc --- /dev/null +++ b/examples/http2/testdata/server.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx +MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50 +ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco +LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg +zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd +9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw +CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy +em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G +CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6 +hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh +y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8 +-----END CERTIFICATE----- From 02a6f9b6bc8ffba2e8b55ca78d5a9448ea8aef1e Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Tue, 11 Jul 2017 13:59:03 +0800 Subject: [PATCH 054/113] chore(vendor): update json-iterator revison to fix TestRenderIndentedJSON failed (#1003) * update json-iterator revison to fix TestRenderIndentedJSON failed * fix(test): add space between key and value as same as standard JSON. * fix(test): add space between key and value as same as standard JSON. --- context_test.go | 2 +- vendor/vendor.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/context_test.go b/context_test.go index f57fa6fd..758fecdc 100644 --- a/context_test.go +++ b/context_test.go @@ -582,7 +582,7 @@ func TestContextRenderIndentedJSON(t *testing.T) { c.IndentedJSON(201, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}) assert.Equal(t, w.Code, 201) - assert.Equal(t, "{\n \"bar\":\"foo\",\n \"foo\":\"bar\",\n \"nested\":{\n \"foo\":\"bar\"\n }\n}", w.Body.String()) + 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")) } diff --git a/vendor/vendor.json b/vendor/vendor.json index cb752e91..3af3cb55 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -34,10 +34,10 @@ "revisionTime": "2017-06-01T23:02:30Z" }, { - "checksumSHA1": "YY//OXgGrJOsiR8Cz1xexk/zwkc=", + "checksumSHA1": "bgb/lk2wroBJ5z+JI2xnVj7WkwI=", "path": "github.com/json-iterator/go", - "revision": "37ba1b32b5ed52dfaff0fe50d923d210c11c8daf", - "revisionTime": "2017-07-08T17:01:13Z" + "revision": "845d8438db34cc782608bbee7647a522b4e87de0", + "revisionTime": "2017-07-10T17:07:18Z" }, { "checksumSHA1": "9if9IBLsxkarJ804NPWAzgskIAk=", From aa6d2d29f8ac39c365aa5b72fdec01029b0d5264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 11 Jul 2017 23:28:08 +0800 Subject: [PATCH 055/113] refactor(doc): use space not tab (#1006) --- context.go | 63 +++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/context.go b/context.go index acf2cd23..198dd3e3 100644 --- a/context.go +++ b/context.go @@ -85,8 +85,8 @@ func (c *Context) Copy() *Context { return &cp } -// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", this -// function will return "main.handleGetUsers" +// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", +// this function will return "main.handleGetUsers" func (c *Context) HandlerName() string { return nameOfFunction(c.handlers.Last()) } @@ -117,8 +117,8 @@ func (c *Context) IsAborted() bool { } // Abort prevents pending handlers from being called. Note that this will not stop the current handler. -// Let's say you have an authorization middleware that validates that the current request is authorized. If the -// authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers +// Let's say you have an authorization middleware that validates that the current request is authorized. +// If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers // for this request are not called. func (c *Context) Abort() { c.index = abortIndex @@ -132,15 +132,16 @@ func (c *Context) AbortWithStatus(code int) { c.Abort() } -// AbortWithStatusJSON calls `Abort()` and then `JSON` internally. This method stops the chain, writes the status code and return a JSON body +// 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{}) { c.Abort() c.JSON(code, jsonObj) } -// AbortWithError calls `AbortWithStatus()` and `Error()` internally. This method stops the chain, writes the status code and -// pushes the specified error to `c.Errors`. +// AbortWithError calls `AbortWithStatus()` and `Error()` internally. +// This method stops the chain, writes the status code and pushes the specified error to `c.Errors`. // See Context.Error() for more details. func (c *Context) AbortWithError(code int, err error) *Error { c.AbortWithStatus(code) @@ -153,8 +154,8 @@ func (c *Context) AbortWithError(code int, err error) *Error { // Error attaches an error to the current context. The error is pushed to a list of errors. // It's a good idea to call Error for each error that occurred during the resolution of a request. -// A middleware can be used to collect all the errors -// and push them to a database together, print a log, or append it in the HTTP response. +// A middleware can be used to collect all the errors and push them to a database together, +// print a log, or append it in the HTTP response. // Error will panic if err is nil. func (c *Context) Error(err error) *Error { if err == nil { @@ -296,10 +297,10 @@ 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) } @@ -307,11 +308,11 @@ func (c *Context) Param(key string) 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) string { value, _ := c.GetQuery(key) return value @@ -320,10 +321,10 @@ func (c *Context) Query(key string) 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 @@ -335,10 +336,10 @@ 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 @@ -384,9 +385,9 @@ 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 @@ -432,8 +433,8 @@ func (c *Context) MultipartForm() (*multipart.Form, error) { // Bind checks the Content-Type to select a binding engine automatically, // Depending the "Content-Type" header different bindings are used: -// "application/json" --> JSON binding -// "application/xml" --> XML binding +// "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. From bbd4dfee5056087c640a75c6cc21567f4f47585d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Wed, 12 Jul 2017 15:01:46 +0800 Subject: [PATCH 056/113] add warning using http2 example (#1009) --- examples/http2/main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/http2/main.go b/examples/http2/main.go index 19e65f84..07df01e2 100644 --- a/examples/http2/main.go +++ b/examples/http2/main.go @@ -2,6 +2,8 @@ package main import ( "html/template" + "log" + "os" "github.com/gin-gonic/gin" ) @@ -18,6 +20,9 @@ var html = template.Must(template.New("https").Parse(` `)) func main() { + logger := log.New(os.Stderr, "", 0) + logger.Println("[WARNING] DON'T USE THE EMBED CERTS FROM THIS EXAMPLE IN PRODUCTION ENVIRONMENT, GENERATE YOUR OWN!") + r := gin.Default() r.SetHTMLTemplate(html) From 986049d24c3980e773b15ba271de58046f16200b Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Wed, 12 Jul 2017 04:26:58 -0500 Subject: [PATCH 057/113] chore(vendor): upgrade jsonite rev (#1011) Signed-off-by: Bo-Yi Wu --- vendor/vendor.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 3af3cb55..822e201f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -34,10 +34,10 @@ "revisionTime": "2017-06-01T23:02:30Z" }, { - "checksumSHA1": "bgb/lk2wroBJ5z+JI2xnVj7WkwI=", + "checksumSHA1": "1K394KhUNPplCZzaz69+az17fR0=", "path": "github.com/json-iterator/go", - "revision": "845d8438db34cc782608bbee7647a522b4e87de0", - "revisionTime": "2017-07-10T17:07:18Z" + "revision": "dc388588a371d252dabef1f758e53a958fa11726", + "revisionTime": "2017-07-12T08:40:14Z" }, { "checksumSHA1": "9if9IBLsxkarJ804NPWAzgskIAk=", From 65a6dd46a50bd435597b5bcababd1b2a811c9073 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Wed, 12 Jul 2017 13:02:00 +0200 Subject: [PATCH 058/113] chore(vendor): update jsonite rev, #1010 (#1012) --- vendor/vendor.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 822e201f..29141c4c 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -34,10 +34,10 @@ "revisionTime": "2017-06-01T23:02:30Z" }, { - "checksumSHA1": "1K394KhUNPplCZzaz69+az17fR0=", + "checksumSHA1": "WFJPa8cL6nzQU3yA1iN+gmaqrSU=", "path": "github.com/json-iterator/go", - "revision": "dc388588a371d252dabef1f758e53a958fa11726", - "revisionTime": "2017-07-12T08:40:14Z" + "revision": "4b33139ad07fda872cb378bb4218b2fab74ce62b", + "revisionTime": "2017-07-12T09:56:51Z" }, { "checksumSHA1": "9if9IBLsxkarJ804NPWAzgskIAk=", From fe4d405108edbe697305be4bfa86ad8fde64a975 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Fri, 14 Jul 2017 18:52:16 +0200 Subject: [PATCH 059/113] docs(readme): fix single file example (#1017) issue discovered by @sgon00 at gitter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 05b696df..d4dcb9d2 100644 --- a/README.md +++ b/README.md @@ -276,7 +276,7 @@ func main() { file, _ := c.FormFile("file") log.Println(file.Filename) - c.String(http.StatusOK, fmt.Printf("'%s' uploaded!", file.Filename)) + c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) }) router.Run(":8080") } From 5cb25a6410a299dcc31e7f31b41c60c90e30b3f6 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Sat, 15 Jul 2017 01:28:16 +0800 Subject: [PATCH 060/113] docs(readme): fix multiple file example (#1018) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4dcb9d2..86001aed 100644 --- a/README.md +++ b/README.md @@ -305,7 +305,7 @@ func main() { for _, file := range files { log.Println(file.Filename) } - c.String(http.StatusOK, fmt.Printf("%d files uploaded!", len(files))) + c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) }) router.Run(":8080") } From 93b3a0d7ec95c33dc327397ab1756c36853328ee Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Sun, 16 Jul 2017 11:42:08 +0800 Subject: [PATCH 061/113] feat(context): add SaveUploadedFile func. (#1022) * feat(context): add SaveUploadedFile func. * feat(context): update multiple upload examples. * style(example): fix gofmt * fix(example): add missing return --- README.md | 8 ++++- context.go | 19 ++++++++++++ context_test.go | 42 +++++++++++++++++++++++++++ examples/upload-file/multiple/main.go | 20 ++----------- examples/upload-file/single/main.go | 20 ++----------- 5 files changed, 73 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 86001aed..ccc5bcc5 100644 --- a/README.md +++ b/README.md @@ -275,7 +275,10 @@ func main() { // 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") @@ -304,6 +307,9 @@ func main() { 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))) }) diff --git a/context.go b/context.go index 198dd3e3..f29464d7 100644 --- a/context.go +++ b/context.go @@ -13,6 +13,7 @@ import ( "net" "net/http" "net/url" + "os" "strings" "time" @@ -431,6 +432,24 @@ func (c *Context) MultipartForm() (*multipart.Form, error) { return c.Request.MultipartForm, err } +// SaveUploadedFile uploads the form file to specific dst. +func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error { + src, err := file.Open() + if err != nil { + return err + } + defer src.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + io.Copy(out, src) + return nil +} + // Bind checks the Content-Type to select a binding engine automatically, // Depending the "Content-Type" header different bindings are used: // "application/json" --> JSON binding diff --git a/context_test.go b/context_test.go index 758fecdc..db960fb3 100644 --- a/context_test.go +++ b/context_test.go @@ -72,12 +72,18 @@ func TestContextFormFile(t *testing.T) { if assert.NoError(t, err) { assert.Equal(t, "test", f.Filename) } + + assert.NoError(t, c.SaveUploadedFile(f, "test")) } func TestContextMultipartForm(t *testing.T) { buf := new(bytes.Buffer) mw := multipart.NewWriter(buf) mw.WriteField("foo", "bar") + w, err := mw.CreateFormFile("file", "test") + if assert.NoError(t, err) { + w.Write([]byte("test")) + } mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", buf) @@ -86,6 +92,42 @@ func TestContextMultipartForm(t *testing.T) { if assert.NoError(t, err) { assert.NotNil(t, f) } + + assert.NoError(t, c.SaveUploadedFile(f.File["file"][0], "test")) +} + +func TestSaveUploadedOpenFailed(t *testing.T) { + buf := new(bytes.Buffer) + mw := multipart.NewWriter(buf) + mw.Close() + + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request.Header.Set("Content-Type", mw.FormDataContentType()) + + f := &multipart.FileHeader{ + Filename: "file", + } + assert.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) { + w.Write([]byte("test")) + } + 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) + } + + assert.Error(t, c.SaveUploadedFile(f, "/")) } func TestContextReset(t *testing.T) { diff --git a/examples/upload-file/multiple/main.go b/examples/upload-file/multiple/main.go index 22588348..4bb4cdcb 100644 --- a/examples/upload-file/multiple/main.go +++ b/examples/upload-file/multiple/main.go @@ -2,9 +2,7 @@ package main import ( "fmt" - "io" "net/http" - "os" "github.com/gin-gonic/gin" ) @@ -25,24 +23,10 @@ func main() { files := form.File["files"] for _, file := range files { - // Source - src, err := file.Open() - if err != nil { - c.String(http.StatusBadRequest, fmt.Sprintf("file open err: %s", err.Error())) + if err := c.SaveUploadedFile(file, file.Filename); err != nil { + c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error())) return } - defer src.Close() - - // Destination - dst, err := os.Create(file.Filename) - if err != nil { - c.String(http.StatusBadRequest, fmt.Sprintf("Create file err: %s", err.Error())) - return - } - defer dst.Close() - - // Copy - io.Copy(dst, src) } c.String(http.StatusOK, fmt.Sprintf("Uploaded successfully %d files with fields name=%s and email=%s.", len(files), name, email)) diff --git a/examples/upload-file/single/main.go b/examples/upload-file/single/main.go index 1e9596cb..372a2994 100644 --- a/examples/upload-file/single/main.go +++ b/examples/upload-file/single/main.go @@ -2,9 +2,7 @@ package main import ( "fmt" - "io" "net/http" - "os" "github.com/gin-gonic/gin" ) @@ -22,23 +20,11 @@ func main() { c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error())) return } - src, err := file.Open() - if err != nil { - c.String(http.StatusBadRequest, fmt.Sprintf("file open err: %s", err.Error())) + + if err := c.SaveUploadedFile(file, file.Filename); err != nil { + c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error())) return } - defer src.Close() - - // Destination - dst, err := os.Create(file.Filename) - if err != nil { - c.String(http.StatusBadRequest, fmt.Sprintf("Create file err: %s", err.Error())) - return - } - defer dst.Close() - - // Copy - io.Copy(dst, src) c.String(http.StatusOK, fmt.Sprintf("File %s uploaded successfully with fields name=%s and email=%s.", file.Filename, name, email)) }) From 8678b3df962ee52c8795802b4bee3c439cc9aeda Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Mon, 17 Jul 2017 02:50:45 +0200 Subject: [PATCH 062/113] docs(readme): add reference to validator.v8 docs, close #738 (#1024) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ccc5bcc5..211ed59d 100644 --- a/README.md +++ b/README.md @@ -406,6 +406,8 @@ func main() { To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz). +Gin uses [**go-playground/validator.v8**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](http://godoc.org/gopkg.in/go-playground/validator.v8#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"`. 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 BindWith. From 8f861946b028a0b5ed4acf8df4b673e23e8c5634 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Mon, 17 Jul 2017 18:57:59 +0800 Subject: [PATCH 063/113] style(msgpack): remove redundant comments (#1027) --- binding/msgpack.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/binding/msgpack.go b/binding/msgpack.go index 69367175..7faea4b5 100644 --- a/binding/msgpack.go +++ b/binding/msgpack.go @@ -17,12 +17,8 @@ func (msgpackBinding) Name() string { } func (msgpackBinding) Bind(req *http.Request, obj interface{}) error { - if err := codec.NewDecoder(req.Body, new(codec.MsgpackHandle)).Decode(&obj); err != nil { - //var decoder *codec.Decoder = codec.NewDecoder(req.Body, &codec.MsgpackHandle) - //if err := decoder.Decode(&obj); err != nil { return err } return validate(obj) - } From b539606eedd1eabd650b2284394a8ada992fd411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 18 Jul 2017 08:54:38 +0800 Subject: [PATCH 064/113] use return not use else (#1028) --- auth.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/auth.go b/auth.go index 7ebf5576..75a7c892 100644 --- a/auth.go +++ b/auth.go @@ -52,11 +52,13 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc { // Credentials doesn't match, we return 401 and abort handlers chain. c.Header("WWW-Authenticate", realm) c.AbortWithStatus(401) - } else { - // The user credentials was found, set user's id to key AuthUserKey in this context, the userId can be read later using - // c.MustGet(gin.AuthUserKey) - c.Set(AuthUserKey, user) + return } + + // The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using + // c.MustGet(gin.AuthUserKey) + c.Set(AuthUserKey, user) + return } } From 199bbf2ae55c3601425895a0e4a760599db3c6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 18 Jul 2017 09:11:53 +0800 Subject: [PATCH 065/113] refactor(gin): use return not use else for reducing indent (#1031) --- gin.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/gin.go b/gin.go index 8388ac7b..fc9214dc 100644 --- a/gin.go +++ b/gin.go @@ -156,19 +156,21 @@ func (engine *Engine) LoadHTMLGlob(pattern string) { if IsDebugging() { debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern))) engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims} - } else { - templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)) - engine.SetHTMLTemplate(templ) + return } + templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)) + engine.SetHTMLTemplate(templ) + return } func (engine *Engine) LoadHTMLFiles(files ...string) { if IsDebugging() { engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims} - } else { - templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...)) - engine.SetHTMLTemplate(templ) + return } + templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...)) + engine.SetHTMLTemplate(templ) + return } func (engine *Engine) SetHTMLTemplate(templ *template.Template) { From 30cfa590bb2b851d521da0198d3803eca2cc2a4b Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Tue, 18 Jul 2017 09:23:10 +0800 Subject: [PATCH 066/113] dos(upload): fix alignment (#1030) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 211ed59d..7e3df99f 100644 --- a/README.md +++ b/README.md @@ -276,8 +276,8 @@ func main() { 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)) }) @@ -309,7 +309,7 @@ func main() { log.Println(file.Filename) // Upload the file to specific dst. - // c.SaveUploadedFile(file, dst) + // c.SaveUploadedFile(file, dst) } c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) }) From 7180f2ba62d6aa9c81fafbac409a07ebeaa26fff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 18 Jul 2017 11:39:39 +0800 Subject: [PATCH 067/113] not use tmp var (#1032) --- context.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/context.go b/context.go index f29464d7..306e2055 100644 --- a/context.go +++ b/context.go @@ -358,8 +358,7 @@ func (c *Context) QueryArray(key string) []string { // GetQueryArray returns a slice of strings for a given query key, plus // a boolean value whether at least one value exists for the given key. func (c *Context) GetQueryArray(key string) ([]string, bool) { - req := c.Request - if values, ok := req.URL.Query()[key]; ok && len(values) > 0 { + if values, ok := c.Request.URL.Query()[key]; ok && len(values) > 0 { return values, true } return []string{}, false From ce670a64977a513d8caa58c143471ddbbf641bd4 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 18 Jul 2017 16:01:29 -0500 Subject: [PATCH 068/113] refactor(json): make jsonite optional with build tags (#1026) * refactor(json): Restore gin support for app engine Create new folder to support multiple json package. restore gin support for app engine (disable jsonite through tags) use jsoniter $ go build -tags=jsoniter . use default json $ go build . Signed-off-by: Bo-Yi Wu * rename json file. Signed-off-by: Bo-Yi Wu * docs(json): add build tags document. * fix(docs): markdown format. * fix(json): missing space. --- README.md | 9 ++++++++- binding/json.go | 3 +-- errors.go | 4 +--- errors_test.go | 1 + json/json.go | 17 +++++++++++++++++ json/jsoniter.go | 18 ++++++++++++++++++ render/json.go | 4 +--- 7 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 json/json.go create mode 100644 json/jsoniter.go diff --git a/README.md b/README.md index 7e3df99f..76ab3b01 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,6 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648 - [x] Battle tested - [x] API frozen, new releases will not break your code. - ## Start using it 1. Download and install it: @@ -141,6 +140,14 @@ $ curl https://raw.githubusercontent.com/gin-gonic/gin/master/examples/basic/mai $ go run main.go ``` +## Build with [jsoniter](https://github.com/json-iterator/go) + +Gin use `encoding/json` as default json package but you can change to [jsoniter](https://github.com/json-iterator/go) by build from other tags. + +```sh +$ go build -tags=jsoniter . +``` + ## API Examples ### Using GET, POST, PUT, PATCH, DELETE and OPTIONS diff --git a/binding/json.go b/binding/json.go index 215fa9bf..f600a5f4 100644 --- a/binding/json.go +++ b/binding/json.go @@ -7,11 +7,10 @@ package binding import ( "net/http" - "github.com/json-iterator/go" + "github.com/gin-gonic/gin/json" ) var ( - json = jsoniter.ConfigCompatibleWithStandardLibrary EnableDecoderUseNumber = false ) diff --git a/errors.go b/errors.go index 26d4e474..19761106 100644 --- a/errors.go +++ b/errors.go @@ -9,11 +9,9 @@ import ( "fmt" "reflect" - "github.com/json-iterator/go" + "github.com/gin-gonic/gin/json" ) -var json = jsoniter.ConfigCompatibleWithStandardLibrary - type ErrorType uint64 const ( diff --git a/errors_test.go b/errors_test.go index a0d95504..5e596aff 100644 --- a/errors_test.go +++ b/errors_test.go @@ -8,6 +8,7 @@ import ( "errors" "testing" + "github.com/gin-gonic/gin/json" "github.com/stretchr/testify/assert" ) diff --git a/json/json.go b/json/json.go new file mode 100644 index 00000000..d2d0f8b3 --- /dev/null +++ b/json/json.go @@ -0,0 +1,17 @@ +// 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. + +// +build !jsoniter + +package json + +import ( + "encoding/json" +) + +var ( + Marshal = json.Marshal + MarshalIndent = json.MarshalIndent + NewDecoder = json.NewDecoder +) diff --git a/json/jsoniter.go b/json/jsoniter.go new file mode 100644 index 00000000..65deee59 --- /dev/null +++ b/json/jsoniter.go @@ -0,0 +1,18 @@ +// 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. + +// +build jsoniter + +package json + +import ( + "github.com/json-iterator/go" +) + +var ( + json = jsoniter.ConfigCompatibleWithStandardLibrary + Marshal = json.Marshal + MarshalIndent = json.MarshalIndent + NewDecoder = json.NewDecoder +) diff --git a/render/json.go b/render/json.go index 17e6ac86..eb2548e2 100644 --- a/render/json.go +++ b/render/json.go @@ -8,11 +8,9 @@ import ( "bytes" "net/http" - "github.com/json-iterator/go" + "github.com/gin-gonic/gin/json" ) -var json = jsoniter.ConfigCompatibleWithStandardLibrary - type JSON struct { Data interface{} } From 88566b928cabbe45f0e6e9ae4f8fad97fc8ae9d4 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 18 Jul 2017 16:06:14 -0500 Subject: [PATCH 069/113] chore(vendor): update jsoniter package (#1033) --- vendor/vendor.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 29141c4c..e8690a2c 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -34,10 +34,10 @@ "revisionTime": "2017-06-01T23:02:30Z" }, { - "checksumSHA1": "WFJPa8cL6nzQU3yA1iN+gmaqrSU=", + "checksumSHA1": "0e59uuETpidkmpaRwipQ8auqwhM=", "path": "github.com/json-iterator/go", - "revision": "4b33139ad07fda872cb378bb4218b2fab74ce62b", - "revisionTime": "2017-07-12T09:56:51Z" + "revision": "6b6938829d6156d7b9825f83eec757f0f571c981", + "revisionTime": "2017-07-18T14:19:52Z" }, { "checksumSHA1": "9if9IBLsxkarJ804NPWAzgskIAk=", From 74221b8a35319c6aaf1b7826069fb19302e3276e Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Wed, 19 Jul 2017 02:40:05 -0500 Subject: [PATCH 070/113] docs(benchmark): update benchmark data (#1035) * feat(Benchmark): update benchmark data * fix format. Signed-off-by: Bo-Yi Wu * docs(benchmarks): re indent column, feature gin * docs(benchmark): fix newline on vm details * docs(readme): beautify bench table, explain result --- BENCHMARKS.md | 896 +++++++++++++++++++++++++++++++++----------------- README.md | 67 ++-- 2 files changed, 634 insertions(+), 329 deletions(-) diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 6efe3ca4..9a7df86a 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -1,298 +1,604 @@ -**Machine:** intel i7 ivy bridge quad-core. 8GB RAM. -**Date:** June 4th, 2015 -[https://github.com/gin-gonic/go-http-routing-benchmark](https://github.com/gin-gonic/go-http-routing-benchmark) + +## Benchmark System + +**VM HOST:** DigitalOcean +**Machine:** 4 CPU, 8 GB RAM. Ubuntu 16.04.2 x64 +**Date:** July 19th, 2017 +**Go Version:** 1.8.3 linux/amd64 +**Source:** [Go HTTP Router Benchmark](https://github.com/julienschmidt/go-http-routing-benchmark) + +## Static Routes: 157 ``` -BenchmarkAce_Param 5000000 372 ns/op 32 B/op 1 allocs/op -BenchmarkBear_Param 1000000 1165 ns/op 424 B/op 5 allocs/op -BenchmarkBeego_Param 1000000 2440 ns/op 720 B/op 10 allocs/op -BenchmarkBone_Param 1000000 1067 ns/op 384 B/op 3 allocs/op -BenchmarkDenco_Param 5000000 240 ns/op 32 B/op 1 allocs/op -BenchmarkEcho_Param 10000000 130 ns/op 0 B/op 0 allocs/op -BenchmarkGin_Param 10000000 133 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_Param 1000000 1826 ns/op 656 B/op 9 allocs/op -BenchmarkGoji_Param 2000000 957 ns/op 336 B/op 2 allocs/op -BenchmarkGoJsonRest_Param 1000000 2021 ns/op 657 B/op 14 allocs/op -BenchmarkGoRestful_Param 200000 8825 ns/op 2496 B/op 31 allocs/op -BenchmarkGorillaMux_Param 500000 3340 ns/op 784 B/op 9 allocs/op -BenchmarkHttpRouter_Param 10000000 152 ns/op 32 B/op 1 allocs/op -BenchmarkHttpTreeMux_Param 2000000 717 ns/op 336 B/op 2 allocs/op -BenchmarkKocha_Param 3000000 423 ns/op 56 B/op 3 allocs/op -BenchmarkMacaron_Param 1000000 3410 ns/op 1104 B/op 11 allocs/op -BenchmarkMartini_Param 200000 7101 ns/op 1152 B/op 12 allocs/op -BenchmarkPat_Param 1000000 2040 ns/op 656 B/op 14 allocs/op -BenchmarkPossum_Param 1000000 2048 ns/op 624 B/op 7 allocs/op -BenchmarkR2router_Param 1000000 1144 ns/op 432 B/op 6 allocs/op -BenchmarkRevel_Param 200000 6725 ns/op 1672 B/op 28 allocs/op -BenchmarkRivet_Param 1000000 1121 ns/op 464 B/op 5 allocs/op -BenchmarkTango_Param 1000000 1479 ns/op 256 B/op 10 allocs/op -BenchmarkTigerTonic_Param 1000000 3393 ns/op 992 B/op 19 allocs/op -BenchmarkTraffic_Param 300000 5525 ns/op 1984 B/op 23 allocs/op -BenchmarkVulcan_Param 2000000 924 ns/op 98 B/op 3 allocs/op -BenchmarkZeus_Param 1000000 1084 ns/op 368 B/op 3 allocs/op -BenchmarkAce_Param5 3000000 614 ns/op 160 B/op 1 allocs/op -BenchmarkBear_Param5 1000000 1617 ns/op 469 B/op 5 allocs/op -BenchmarkBeego_Param5 1000000 3373 ns/op 992 B/op 13 allocs/op -BenchmarkBone_Param5 1000000 1478 ns/op 432 B/op 3 allocs/op -BenchmarkDenco_Param5 3000000 570 ns/op 160 B/op 1 allocs/op -BenchmarkEcho_Param5 5000000 256 ns/op 0 B/op 0 allocs/op -BenchmarkGin_Param5 10000000 222 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_Param5 1000000 2789 ns/op 928 B/op 12 allocs/op -BenchmarkGoji_Param5 1000000 1287 ns/op 336 B/op 2 allocs/op -BenchmarkGoJsonRest_Param5 1000000 3670 ns/op 1105 B/op 17 allocs/op -BenchmarkGoRestful_Param5 200000 10756 ns/op 2672 B/op 31 allocs/op -BenchmarkGorillaMux_Param5 300000 5543 ns/op 912 B/op 9 allocs/op -BenchmarkHttpRouter_Param5 5000000 403 ns/op 160 B/op 1 allocs/op -BenchmarkHttpTreeMux_Param5 1000000 1089 ns/op 336 B/op 2 allocs/op -BenchmarkKocha_Param5 1000000 1682 ns/op 440 B/op 10 allocs/op -BenchmarkMacaron_Param5 300000 4596 ns/op 1376 B/op 14 allocs/op -BenchmarkMartini_Param5 100000 15703 ns/op 1280 B/op 12 allocs/op -BenchmarkPat_Param5 300000 5320 ns/op 1008 B/op 42 allocs/op -BenchmarkPossum_Param5 1000000 2155 ns/op 624 B/op 7 allocs/op -BenchmarkR2router_Param5 1000000 1559 ns/op 432 B/op 6 allocs/op -BenchmarkRevel_Param5 200000 8184 ns/op 2024 B/op 35 allocs/op -BenchmarkRivet_Param5 1000000 1914 ns/op 528 B/op 9 allocs/op -BenchmarkTango_Param5 1000000 3280 ns/op 944 B/op 18 allocs/op -BenchmarkTigerTonic_Param5 200000 11638 ns/op 2519 B/op 53 allocs/op -BenchmarkTraffic_Param5 200000 8941 ns/op 2280 B/op 31 allocs/op -BenchmarkVulcan_Param5 1000000 1279 ns/op 98 B/op 3 allocs/op -BenchmarkZeus_Param5 1000000 1574 ns/op 416 B/op 3 allocs/op -BenchmarkAce_Param20 1000000 1528 ns/op 640 B/op 1 allocs/op -BenchmarkBear_Param20 300000 4906 ns/op 1633 B/op 5 allocs/op -BenchmarkBeego_Param20 200000 10529 ns/op 3868 B/op 17 allocs/op -BenchmarkBone_Param20 300000 7362 ns/op 2539 B/op 5 allocs/op -BenchmarkDenco_Param20 1000000 1884 ns/op 640 B/op 1 allocs/op -BenchmarkEcho_Param20 2000000 689 ns/op 0 B/op 0 allocs/op -BenchmarkGin_Param20 3000000 545 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_Param20 200000 9437 ns/op 3804 B/op 16 allocs/op -BenchmarkGoji_Param20 500000 3987 ns/op 1246 B/op 2 allocs/op -BenchmarkGoJsonRest_Param20 100000 12799 ns/op 4492 B/op 21 allocs/op -BenchmarkGoRestful_Param20 100000 19451 ns/op 5244 B/op 33 allocs/op -BenchmarkGorillaMux_Param20 100000 12456 ns/op 3275 B/op 11 allocs/op -BenchmarkHttpRouter_Param20 1000000 1333 ns/op 640 B/op 1 allocs/op -BenchmarkHttpTreeMux_Param20 300000 6490 ns/op 2187 B/op 4 allocs/op -BenchmarkKocha_Param20 300000 5335 ns/op 1808 B/op 27 allocs/op -BenchmarkMacaron_Param20 200000 11325 ns/op 4252 B/op 18 allocs/op -BenchmarkMartini_Param20 20000 64419 ns/op 3644 B/op 14 allocs/op -BenchmarkPat_Param20 50000 24672 ns/op 4888 B/op 151 allocs/op -BenchmarkPossum_Param20 1000000 2085 ns/op 624 B/op 7 allocs/op -BenchmarkR2router_Param20 300000 6809 ns/op 2283 B/op 8 allocs/op -BenchmarkRevel_Param20 100000 16600 ns/op 5551 B/op 54 allocs/op -BenchmarkRivet_Param20 200000 8428 ns/op 2620 B/op 26 allocs/op -BenchmarkTango_Param20 100000 16302 ns/op 8224 B/op 48 allocs/op -BenchmarkTigerTonic_Param20 30000 46828 ns/op 10538 B/op 178 allocs/op -BenchmarkTraffic_Param20 50000 28871 ns/op 7998 B/op 66 allocs/op -BenchmarkVulcan_Param20 1000000 2267 ns/op 98 B/op 3 allocs/op -BenchmarkZeus_Param20 300000 6828 ns/op 2507 B/op 5 allocs/op -BenchmarkAce_ParamWrite 3000000 502 ns/op 40 B/op 2 allocs/op -BenchmarkBear_ParamWrite 1000000 1303 ns/op 424 B/op 5 allocs/op -BenchmarkBeego_ParamWrite 1000000 2489 ns/op 728 B/op 11 allocs/op -BenchmarkBone_ParamWrite 1000000 1181 ns/op 384 B/op 3 allocs/op -BenchmarkDenco_ParamWrite 5000000 315 ns/op 32 B/op 1 allocs/op -BenchmarkEcho_ParamWrite 10000000 237 ns/op 8 B/op 1 allocs/op -BenchmarkGin_ParamWrite 5000000 336 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_ParamWrite 1000000 2079 ns/op 664 B/op 10 allocs/op -BenchmarkGoji_ParamWrite 1000000 1092 ns/op 336 B/op 2 allocs/op -BenchmarkGoJsonRest_ParamWrite 1000000 3329 ns/op 1136 B/op 19 allocs/op -BenchmarkGoRestful_ParamWrite 200000 9273 ns/op 2504 B/op 32 allocs/op -BenchmarkGorillaMux_ParamWrite 500000 3919 ns/op 792 B/op 10 allocs/op -BenchmarkHttpRouter_ParamWrite 10000000 223 ns/op 32 B/op 1 allocs/op -BenchmarkHttpTreeMux_ParamWrite 2000000 788 ns/op 336 B/op 2 allocs/op -BenchmarkKocha_ParamWrite 3000000 549 ns/op 56 B/op 3 allocs/op -BenchmarkMacaron_ParamWrite 500000 4558 ns/op 1216 B/op 16 allocs/op -BenchmarkMartini_ParamWrite 200000 8850 ns/op 1256 B/op 16 allocs/op -BenchmarkPat_ParamWrite 500000 3679 ns/op 1088 B/op 19 allocs/op -BenchmarkPossum_ParamWrite 1000000 2114 ns/op 624 B/op 7 allocs/op -BenchmarkR2router_ParamWrite 1000000 1320 ns/op 432 B/op 6 allocs/op -BenchmarkRevel_ParamWrite 200000 8048 ns/op 2128 B/op 33 allocs/op -BenchmarkRivet_ParamWrite 1000000 1393 ns/op 472 B/op 6 allocs/op -BenchmarkTango_ParamWrite 2000000 819 ns/op 136 B/op 5 allocs/op -BenchmarkTigerTonic_ParamWrite 300000 5860 ns/op 1440 B/op 25 allocs/op -BenchmarkTraffic_ParamWrite 200000 7429 ns/op 2400 B/op 27 allocs/op -BenchmarkVulcan_ParamWrite 2000000 972 ns/op 98 B/op 3 allocs/op -BenchmarkZeus_ParamWrite 1000000 1226 ns/op 368 B/op 3 allocs/op -BenchmarkAce_GithubStatic 5000000 294 ns/op 0 B/op 0 allocs/op -BenchmarkBear_GithubStatic 3000000 575 ns/op 88 B/op 3 allocs/op -BenchmarkBeego_GithubStatic 1000000 1561 ns/op 368 B/op 7 allocs/op -BenchmarkBone_GithubStatic 200000 12301 ns/op 2880 B/op 60 allocs/op -BenchmarkDenco_GithubStatic 20000000 74.6 ns/op 0 B/op 0 allocs/op -BenchmarkEcho_GithubStatic 10000000 176 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GithubStatic 10000000 159 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GithubStatic 1000000 1116 ns/op 304 B/op 6 allocs/op -BenchmarkGoji_GithubStatic 5000000 413 ns/op 0 B/op 0 allocs/op -BenchmarkGoRestful_GithubStatic 30000 55200 ns/op 3520 B/op 36 allocs/op -BenchmarkGoJsonRest_GithubStatic 1000000 1504 ns/op 337 B/op 12 allocs/op -BenchmarkGorillaMux_GithubStatic 100000 23620 ns/op 464 B/op 8 allocs/op -BenchmarkHttpRouter_GithubStatic 20000000 78.3 ns/op 0 B/op 0 allocs/op -BenchmarkHttpTreeMux_GithubStatic 20000000 84.9 ns/op 0 B/op 0 allocs/op -BenchmarkKocha_GithubStatic 20000000 111 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_GithubStatic 1000000 2686 ns/op 752 B/op 8 allocs/op -BenchmarkMartini_GithubStatic 100000 22244 ns/op 832 B/op 11 allocs/op -BenchmarkPat_GithubStatic 100000 13278 ns/op 3648 B/op 76 allocs/op -BenchmarkPossum_GithubStatic 1000000 1429 ns/op 480 B/op 4 allocs/op -BenchmarkR2router_GithubStatic 2000000 726 ns/op 144 B/op 5 allocs/op -BenchmarkRevel_GithubStatic 300000 6271 ns/op 1288 B/op 25 allocs/op -BenchmarkRivet_GithubStatic 3000000 474 ns/op 112 B/op 2 allocs/op -BenchmarkTango_GithubStatic 1000000 1842 ns/op 256 B/op 10 allocs/op -BenchmarkTigerTonic_GithubStatic 5000000 361 ns/op 48 B/op 1 allocs/op -BenchmarkTraffic_GithubStatic 30000 47197 ns/op 18920 B/op 149 allocs/op -BenchmarkVulcan_GithubStatic 1000000 1415 ns/op 98 B/op 3 allocs/op -BenchmarkZeus_GithubStatic 1000000 2522 ns/op 512 B/op 11 allocs/op -BenchmarkAce_GithubParam 3000000 578 ns/op 96 B/op 1 allocs/op -BenchmarkBear_GithubParam 1000000 1592 ns/op 464 B/op 5 allocs/op -BenchmarkBeego_GithubParam 1000000 2891 ns/op 784 B/op 11 allocs/op -BenchmarkBone_GithubParam 300000 6440 ns/op 1456 B/op 16 allocs/op -BenchmarkDenco_GithubParam 3000000 514 ns/op 128 B/op 1 allocs/op -BenchmarkEcho_GithubParam 5000000 292 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GithubParam 10000000 242 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GithubParam 1000000 2343 ns/op 720 B/op 10 allocs/op -BenchmarkGoji_GithubParam 1000000 1566 ns/op 336 B/op 2 allocs/op -BenchmarkGoJsonRest_GithubParam 1000000 2828 ns/op 721 B/op 15 allocs/op -BenchmarkGoRestful_GithubParam 10000 177711 ns/op 2816 B/op 35 allocs/op -BenchmarkGorillaMux_GithubParam 100000 13591 ns/op 816 B/op 9 allocs/op -BenchmarkHttpRouter_GithubParam 5000000 352 ns/op 96 B/op 1 allocs/op -BenchmarkHttpTreeMux_GithubParam 2000000 973 ns/op 336 B/op 2 allocs/op -BenchmarkKocha_GithubParam 2000000 889 ns/op 128 B/op 5 allocs/op -BenchmarkMacaron_GithubParam 500000 4047 ns/op 1168 B/op 12 allocs/op -BenchmarkMartini_GithubParam 50000 28982 ns/op 1184 B/op 12 allocs/op -BenchmarkPat_GithubParam 200000 8747 ns/op 2480 B/op 56 allocs/op -BenchmarkPossum_GithubParam 1000000 2158 ns/op 624 B/op 7 allocs/op -BenchmarkR2router_GithubParam 1000000 1352 ns/op 432 B/op 6 allocs/op -BenchmarkRevel_GithubParam 200000 7673 ns/op 1784 B/op 30 allocs/op -BenchmarkRivet_GithubParam 1000000 1573 ns/op 480 B/op 6 allocs/op -BenchmarkTango_GithubParam 1000000 2418 ns/op 480 B/op 13 allocs/op -BenchmarkTigerTonic_GithubParam 300000 6048 ns/op 1440 B/op 28 allocs/op -BenchmarkTraffic_GithubParam 100000 20143 ns/op 6024 B/op 55 allocs/op -BenchmarkVulcan_GithubParam 1000000 2224 ns/op 98 B/op 3 allocs/op -BenchmarkZeus_GithubParam 500000 4156 ns/op 1312 B/op 12 allocs/op -BenchmarkAce_GithubAll 10000 109482 ns/op 13792 B/op 167 allocs/op -BenchmarkBear_GithubAll 10000 287490 ns/op 79952 B/op 943 allocs/op -BenchmarkBeego_GithubAll 3000 562184 ns/op 146272 B/op 2092 allocs/op -BenchmarkBone_GithubAll 500 2578716 ns/op 648016 B/op 8119 allocs/op -BenchmarkDenco_GithubAll 20000 94955 ns/op 20224 B/op 167 allocs/op -BenchmarkEcho_GithubAll 30000 58705 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GithubAll 30000 50991 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GithubAll 5000 449648 ns/op 133280 B/op 1889 allocs/op -BenchmarkGoji_GithubAll 2000 689748 ns/op 56113 B/op 334 allocs/op -BenchmarkGoJsonRest_GithubAll 5000 537769 ns/op 135995 B/op 2940 allocs/op -BenchmarkGoRestful_GithubAll 100 18410628 ns/op 797236 B/op 7725 allocs/op -BenchmarkGorillaMux_GithubAll 200 8036360 ns/op 153137 B/op 1791 allocs/op -BenchmarkHttpRouter_GithubAll 20000 63506 ns/op 13792 B/op 167 allocs/op -BenchmarkHttpTreeMux_GithubAll 10000 165927 ns/op 56112 B/op 334 allocs/op -BenchmarkKocha_GithubAll 10000 171362 ns/op 23304 B/op 843 allocs/op -BenchmarkMacaron_GithubAll 2000 817008 ns/op 224960 B/op 2315 allocs/op -BenchmarkMartini_GithubAll 100 12609209 ns/op 237952 B/op 2686 allocs/op -BenchmarkPat_GithubAll 300 4830398 ns/op 1504101 B/op 32222 allocs/op -BenchmarkPossum_GithubAll 10000 301716 ns/op 97440 B/op 812 allocs/op -BenchmarkR2router_GithubAll 10000 270691 ns/op 77328 B/op 1182 allocs/op -BenchmarkRevel_GithubAll 1000 1491919 ns/op 345553 B/op 5918 allocs/op -BenchmarkRivet_GithubAll 10000 283860 ns/op 84272 B/op 1079 allocs/op -BenchmarkTango_GithubAll 5000 473821 ns/op 87078 B/op 2470 allocs/op -BenchmarkTigerTonic_GithubAll 2000 1120131 ns/op 241088 B/op 6052 allocs/op -BenchmarkTraffic_GithubAll 200 8708979 ns/op 2664762 B/op 22390 allocs/op -BenchmarkVulcan_GithubAll 5000 353392 ns/op 19894 B/op 609 allocs/op -BenchmarkZeus_GithubAll 2000 944234 ns/op 300688 B/op 2648 allocs/op -BenchmarkAce_GPlusStatic 5000000 251 ns/op 0 B/op 0 allocs/op -BenchmarkBear_GPlusStatic 3000000 415 ns/op 72 B/op 3 allocs/op -BenchmarkBeego_GPlusStatic 1000000 1416 ns/op 352 B/op 7 allocs/op -BenchmarkBone_GPlusStatic 10000000 192 ns/op 32 B/op 1 allocs/op -BenchmarkDenco_GPlusStatic 30000000 47.6 ns/op 0 B/op 0 allocs/op -BenchmarkEcho_GPlusStatic 10000000 131 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GPlusStatic 10000000 131 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GPlusStatic 1000000 1035 ns/op 288 B/op 6 allocs/op -BenchmarkGoji_GPlusStatic 5000000 304 ns/op 0 B/op 0 allocs/op -BenchmarkGoJsonRest_GPlusStatic 1000000 1286 ns/op 337 B/op 12 allocs/op -BenchmarkGoRestful_GPlusStatic 200000 9649 ns/op 2160 B/op 30 allocs/op -BenchmarkGorillaMux_GPlusStatic 1000000 2346 ns/op 464 B/op 8 allocs/op -BenchmarkHttpRouter_GPlusStatic 30000000 42.7 ns/op 0 B/op 0 allocs/op -BenchmarkHttpTreeMux_GPlusStatic 30000000 49.5 ns/op 0 B/op 0 allocs/op -BenchmarkKocha_GPlusStatic 20000000 74.8 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_GPlusStatic 1000000 2520 ns/op 736 B/op 8 allocs/op -BenchmarkMartini_GPlusStatic 300000 5310 ns/op 832 B/op 11 allocs/op -BenchmarkPat_GPlusStatic 5000000 398 ns/op 96 B/op 2 allocs/op -BenchmarkPossum_GPlusStatic 1000000 1434 ns/op 480 B/op 4 allocs/op -BenchmarkR2router_GPlusStatic 2000000 646 ns/op 144 B/op 5 allocs/op -BenchmarkRevel_GPlusStatic 300000 6172 ns/op 1272 B/op 25 allocs/op -BenchmarkRivet_GPlusStatic 3000000 444 ns/op 112 B/op 2 allocs/op -BenchmarkTango_GPlusStatic 1000000 1400 ns/op 208 B/op 10 allocs/op -BenchmarkTigerTonic_GPlusStatic 10000000 213 ns/op 32 B/op 1 allocs/op -BenchmarkTraffic_GPlusStatic 1000000 3091 ns/op 1208 B/op 16 allocs/op -BenchmarkVulcan_GPlusStatic 2000000 863 ns/op 98 B/op 3 allocs/op -BenchmarkZeus_GPlusStatic 10000000 237 ns/op 16 B/op 1 allocs/op -BenchmarkAce_GPlusParam 3000000 435 ns/op 64 B/op 1 allocs/op -BenchmarkBear_GPlusParam 1000000 1205 ns/op 448 B/op 5 allocs/op -BenchmarkBeego_GPlusParam 1000000 2494 ns/op 720 B/op 10 allocs/op -BenchmarkBone_GPlusParam 1000000 1126 ns/op 384 B/op 3 allocs/op -BenchmarkDenco_GPlusParam 5000000 325 ns/op 64 B/op 1 allocs/op -BenchmarkEcho_GPlusParam 10000000 168 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GPlusParam 10000000 170 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GPlusParam 1000000 1895 ns/op 656 B/op 9 allocs/op -BenchmarkGoji_GPlusParam 1000000 1071 ns/op 336 B/op 2 allocs/op -BenchmarkGoJsonRest_GPlusParam 1000000 2282 ns/op 657 B/op 14 allocs/op -BenchmarkGoRestful_GPlusParam 100000 19400 ns/op 2560 B/op 33 allocs/op -BenchmarkGorillaMux_GPlusParam 500000 5001 ns/op 784 B/op 9 allocs/op -BenchmarkHttpRouter_GPlusParam 10000000 240 ns/op 64 B/op 1 allocs/op -BenchmarkHttpTreeMux_GPlusParam 2000000 797 ns/op 336 B/op 2 allocs/op -BenchmarkKocha_GPlusParam 3000000 505 ns/op 56 B/op 3 allocs/op -BenchmarkMacaron_GPlusParam 1000000 3668 ns/op 1104 B/op 11 allocs/op -BenchmarkMartini_GPlusParam 200000 10672 ns/op 1152 B/op 12 allocs/op -BenchmarkPat_GPlusParam 1000000 2376 ns/op 704 B/op 14 allocs/op -BenchmarkPossum_GPlusParam 1000000 2090 ns/op 624 B/op 7 allocs/op -BenchmarkR2router_GPlusParam 1000000 1233 ns/op 432 B/op 6 allocs/op -BenchmarkRevel_GPlusParam 200000 6778 ns/op 1704 B/op 28 allocs/op -BenchmarkRivet_GPlusParam 1000000 1279 ns/op 464 B/op 5 allocs/op -BenchmarkTango_GPlusParam 1000000 1981 ns/op 272 B/op 10 allocs/op -BenchmarkTigerTonic_GPlusParam 500000 3893 ns/op 1064 B/op 19 allocs/op -BenchmarkTraffic_GPlusParam 200000 6585 ns/op 2000 B/op 23 allocs/op -BenchmarkVulcan_GPlusParam 1000000 1233 ns/op 98 B/op 3 allocs/op -BenchmarkZeus_GPlusParam 1000000 1350 ns/op 368 B/op 3 allocs/op -BenchmarkAce_GPlus2Params 3000000 512 ns/op 64 B/op 1 allocs/op -BenchmarkBear_GPlus2Params 1000000 1564 ns/op 464 B/op 5 allocs/op -BenchmarkBeego_GPlus2Params 1000000 3043 ns/op 784 B/op 11 allocs/op -BenchmarkBone_GPlus2Params 1000000 3152 ns/op 736 B/op 7 allocs/op -BenchmarkDenco_GPlus2Params 3000000 431 ns/op 64 B/op 1 allocs/op -BenchmarkEcho_GPlus2Params 5000000 247 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GPlus2Params 10000000 219 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GPlus2Params 1000000 2363 ns/op 720 B/op 10 allocs/op -BenchmarkGoji_GPlus2Params 1000000 1540 ns/op 336 B/op 2 allocs/op -BenchmarkGoJsonRest_GPlus2Params 1000000 2872 ns/op 721 B/op 15 allocs/op -BenchmarkGoRestful_GPlus2Params 100000 23030 ns/op 2720 B/op 35 allocs/op -BenchmarkGorillaMux_GPlus2Params 200000 10516 ns/op 816 B/op 9 allocs/op -BenchmarkHttpRouter_GPlus2Params 5000000 273 ns/op 64 B/op 1 allocs/op -BenchmarkHttpTreeMux_GPlus2Params 2000000 939 ns/op 336 B/op 2 allocs/op -BenchmarkKocha_GPlus2Params 2000000 844 ns/op 128 B/op 5 allocs/op -BenchmarkMacaron_GPlus2Params 500000 3914 ns/op 1168 B/op 12 allocs/op -BenchmarkMartini_GPlus2Params 50000 35759 ns/op 1280 B/op 16 allocs/op -BenchmarkPat_GPlus2Params 200000 7089 ns/op 2304 B/op 41 allocs/op -BenchmarkPossum_GPlus2Params 1000000 2093 ns/op 624 B/op 7 allocs/op -BenchmarkR2router_GPlus2Params 1000000 1320 ns/op 432 B/op 6 allocs/op -BenchmarkRevel_GPlus2Params 200000 7351 ns/op 1800 B/op 30 allocs/op -BenchmarkRivet_GPlus2Params 1000000 1485 ns/op 480 B/op 6 allocs/op -BenchmarkTango_GPlus2Params 1000000 2111 ns/op 448 B/op 12 allocs/op -BenchmarkTigerTonic_GPlus2Params 300000 6271 ns/op 1528 B/op 28 allocs/op -BenchmarkTraffic_GPlus2Params 100000 14886 ns/op 3312 B/op 34 allocs/op -BenchmarkVulcan_GPlus2Params 1000000 1883 ns/op 98 B/op 3 allocs/op -BenchmarkZeus_GPlus2Params 1000000 2686 ns/op 784 B/op 6 allocs/op -BenchmarkAce_GPlusAll 300000 5912 ns/op 640 B/op 11 allocs/op -BenchmarkBear_GPlusAll 100000 16448 ns/op 5072 B/op 61 allocs/op -BenchmarkBeego_GPlusAll 50000 32916 ns/op 8976 B/op 129 allocs/op -BenchmarkBone_GPlusAll 50000 25836 ns/op 6992 B/op 76 allocs/op -BenchmarkDenco_GPlusAll 500000 4462 ns/op 672 B/op 11 allocs/op -BenchmarkEcho_GPlusAll 500000 2806 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GPlusAll 500000 2579 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GPlusAll 50000 25223 ns/op 8144 B/op 116 allocs/op -BenchmarkGoji_GPlusAll 100000 14237 ns/op 3696 B/op 22 allocs/op -BenchmarkGoJsonRest_GPlusAll 50000 29227 ns/op 8221 B/op 183 allocs/op -BenchmarkGoRestful_GPlusAll 10000 203144 ns/op 36064 B/op 441 allocs/op -BenchmarkGorillaMux_GPlusAll 20000 80906 ns/op 9712 B/op 115 allocs/op -BenchmarkHttpRouter_GPlusAll 500000 3040 ns/op 640 B/op 11 allocs/op -BenchmarkHttpTreeMux_GPlusAll 200000 9627 ns/op 3696 B/op 22 allocs/op -BenchmarkKocha_GPlusAll 200000 8108 ns/op 976 B/op 43 allocs/op -BenchmarkMacaron_GPlusAll 30000 48083 ns/op 13968 B/op 142 allocs/op -BenchmarkMartini_GPlusAll 10000 196978 ns/op 15072 B/op 178 allocs/op -BenchmarkPat_GPlusAll 30000 58865 ns/op 16880 B/op 343 allocs/op -BenchmarkPossum_GPlusAll 100000 19685 ns/op 6240 B/op 52 allocs/op -BenchmarkR2router_GPlusAll 100000 16251 ns/op 5040 B/op 76 allocs/op -BenchmarkRevel_GPlusAll 20000 93489 ns/op 21656 B/op 368 allocs/op -BenchmarkRivet_GPlusAll 100000 16907 ns/op 5408 B/op 64 allocs/op +Gin: 30512 Bytes + +HttpServeMux: 17344 Bytes +Ace: 30080 Bytes +Bear: 30472 Bytes +Beego: 96408 Bytes +Bone: 37904 Bytes +Denco: 10464 Bytes +Echo: 73680 Bytes +GocraftWeb: 55720 Bytes +Goji: 27200 Bytes +Gojiv2: 104464 Bytes +GoJsonRest: 136472 Bytes +GoRestful: 914904 Bytes +GorillaMux: 675568 Bytes +HttpRouter: 21128 Bytes +HttpTreeMux: 73448 Bytes +Kocha: 115072 Bytes +LARS: 30120 Bytes +Macaron: 37984 Bytes +Martini: 310832 Bytes +Pat: 20464 Bytes +Possum: 91328 Bytes +R2router: 23712 Bytes +Rivet: 23880 Bytes +Tango: 28008 Bytes +TigerTonic: 80368 Bytes +Traffic: 626480 Bytes +Vulcan: 369064 Bytes +``` + +## GithubAPI Routes: 203 + +``` +Gin: 52672 Bytes + +Ace: 48992 Bytes +Bear: 161592 Bytes +Beego: 147992 Bytes +Bone: 97728 Bytes +Denco: 36440 Bytes +Echo: 95672 Bytes +GocraftWeb: 95640 Bytes +Goji: 86088 Bytes +Gojiv2: 144392 Bytes +GoJsonRest: 134648 Bytes +GoRestful: 1410760 Bytes +GorillaMux: 1509488 Bytes +HttpRouter: 37464 Bytes +HttpTreeMux: 78800 Bytes +Kocha: 785408 Bytes +LARS: 49032 Bytes +Macaron: 132712 Bytes +Martini: 564352 Bytes +Pat: 21200 Bytes +Possum: 83888 Bytes +R2router: 47104 Bytes +Rivet: 42840 Bytes +Tango: 54584 Bytes +TigerTonic: 96384 Bytes +Traffic: 1061920 Bytes +Vulcan: 465296 Bytes +``` + +## GPlusAPI Routes: 13 + +``` +Gin: 3968 Bytes + +Ace: 3600 Bytes +Bear: 7112 Bytes +Beego: 10048 Bytes +Bone: 6480 Bytes +Denco: 3256 Bytes +Echo: 9000 Bytes +GocraftWeb: 7496 Bytes +Goji: 2912 Bytes +Gojiv2: 7376 Bytes +GoJsonRest: 11544 Bytes +GoRestful: 88776 Bytes +GorillaMux: 71488 Bytes +HttpRouter: 2712 Bytes +HttpTreeMux: 7440 Bytes +Kocha: 128880 Bytes +LARS: 3640 Bytes +Macaron: 8656 Bytes +Martini: 23936 Bytes +Pat: 1856 Bytes +Possum: 7248 Bytes +R2router: 3928 Bytes +Rivet: 3064 Bytes +Tango: 4912 Bytes +TigerTonic: 9408 Bytes +Traffic: 49472 Bytes +Vulcan: 25496 Bytes +``` + +## ParseAPI Routes: 26 + +``` +Gin: 6928 Bytes + +Ace: 6592 Bytes +Bear: 12320 Bytes +Beego: 18960 Bytes +Bone: 11024 Bytes +Denco: 4184 Bytes +Echo: 11168 Bytes +GocraftWeb: 12800 Bytes +Goji: 5232 Bytes +Gojiv2: 14464 Bytes +GoJsonRest: 14216 Bytes +GoRestful: 127368 Bytes +GorillaMux: 123016 Bytes +HttpRouter: 4976 Bytes +HttpTreeMux: 7848 Bytes +Kocha: 181712 Bytes +LARS: 6632 Bytes +Macaron: 13648 Bytes +Martini: 45952 Bytes +Pat: 2560 Bytes +Possum: 9200 Bytes +R2router: 7056 Bytes +Rivet: 5680 Bytes +Tango: 8664 Bytes +TigerTonic: 9840 Bytes +Traffic: 93480 Bytes +Vulcan: 44504 Bytes +``` + +## Static Routes + +``` +BenchmarkGin_StaticAll 50000 34506 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_StaticAll 30000 49657 ns/op 0 B/op 0 allocs/op +BenchmarkHttpServeMux_StaticAll 2000 1183737 ns/op 96 B/op 8 allocs/op +BenchmarkBeego_StaticAll 5000 412621 ns/op 57776 B/op 628 allocs/op +BenchmarkBear_StaticAll 10000 149242 ns/op 20336 B/op 461 allocs/op +BenchmarkBone_StaticAll 10000 118583 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_StaticAll 100000 13247 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_StaticAll 20000 79914 ns/op 5024 B/op 157 allocs/op +BenchmarkGocraftWeb_StaticAll 10000 211823 ns/op 46440 B/op 785 allocs/op +BenchmarkGoji_StaticAll 10000 109390 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_StaticAll 3000 415533 ns/op 145696 B/op 1099 allocs/op +BenchmarkGoJsonRest_StaticAll 5000 364403 ns/op 51653 B/op 1727 allocs/op +BenchmarkGoRestful_StaticAll 500 2578579 ns/op 314936 B/op 3144 allocs/op +BenchmarkGorillaMux_StaticAll 500 2704856 ns/op 115648 B/op 1578 allocs/op +BenchmarkHttpRouter_StaticAll 100000 18541 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_StaticAll 100000 22332 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_StaticAll 50000 31176 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_StaticAll 50000 40840 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_StaticAll 5000 517656 ns/op 120576 B/op 1413 allocs/op +BenchmarkMartini_StaticAll 300 4462289 ns/op 125442 B/op 1717 allocs/op +BenchmarkPat_StaticAll 500 2157275 ns/op 533904 B/op 11123 allocs/op +BenchmarkPossum_StaticAll 10000 254701 ns/op 65312 B/op 471 allocs/op +BenchmarkR2router_StaticAll 10000 133956 ns/op 22608 B/op 628 allocs/op +BenchmarkRivet_StaticAll 30000 46812 ns/op 0 B/op 0 allocs/op +BenchmarkTango_StaticAll 5000 390613 ns/op 39225 B/op 1256 allocs/op +BenchmarkTigerTonic_StaticAll 20000 88060 ns/op 7504 B/op 157 allocs/op +BenchmarkTraffic_StaticAll 500 2910236 ns/op 729736 B/op 14287 allocs/op +BenchmarkVulcan_StaticAll 5000 277366 ns/op 15386 B/op 471 allocs/op +``` + +## Micro Benchmarks + +``` +BenchmarkGin_Param 20000000 113 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_Param 5000000 375 ns/op 32 B/op 1 allocs/op +BenchmarkBear_Param 1000000 1709 ns/op 456 B/op 5 allocs/op +BenchmarkBeego_Param 1000000 2484 ns/op 368 B/op 4 allocs/op +BenchmarkBone_Param 1000000 2391 ns/op 688 B/op 5 allocs/op +BenchmarkDenco_Param 10000000 240 ns/op 32 B/op 1 allocs/op +BenchmarkEcho_Param 5000000 366 ns/op 32 B/op 1 allocs/op +BenchmarkGocraftWeb_Param 1000000 2343 ns/op 648 B/op 8 allocs/op +BenchmarkGoji_Param 1000000 1197 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Param 1000000 2771 ns/op 944 B/op 8 allocs/op +BenchmarkGoJsonRest_Param 1000000 2993 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_Param 200000 8860 ns/op 2296 B/op 21 allocs/op +BenchmarkGorillaMux_Param 500000 4461 ns/op 1056 B/op 11 allocs/op +BenchmarkHttpRouter_Param 10000000 175 ns/op 32 B/op 1 allocs/op +BenchmarkHttpTreeMux_Param 1000000 1167 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_Param 3000000 429 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_Param 10000000 134 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param 500000 4635 ns/op 1056 B/op 10 allocs/op +BenchmarkMartini_Param 200000 9933 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_Param 1000000 2929 ns/op 648 B/op 12 allocs/op +BenchmarkPossum_Param 1000000 2503 ns/op 560 B/op 6 allocs/op +BenchmarkR2router_Param 1000000 1507 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Param 5000000 297 ns/op 48 B/op 1 allocs/op +BenchmarkTango_Param 1000000 1862 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_Param 500000 5660 ns/op 992 B/op 17 allocs/op +BenchmarkTraffic_Param 200000 8408 ns/op 1960 B/op 21 allocs/op +BenchmarkVulcan_Param 2000000 963 ns/op 98 B/op 3 allocs/op +BenchmarkAce_Param5 2000000 740 ns/op 160 B/op 1 allocs/op +BenchmarkBear_Param5 1000000 2777 ns/op 501 B/op 5 allocs/op +BenchmarkBeego_Param5 1000000 3740 ns/op 368 B/op 4 allocs/op +BenchmarkBone_Param5 1000000 2950 ns/op 736 B/op 5 allocs/op +BenchmarkDenco_Param5 2000000 644 ns/op 160 B/op 1 allocs/op +BenchmarkEcho_Param5 3000000 558 ns/op 32 B/op 1 allocs/op +BenchmarkGin_Param5 10000000 198 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param5 500000 3870 ns/op 920 B/op 11 allocs/op +BenchmarkGoji_Param5 1000000 1746 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Param5 1000000 3214 ns/op 1008 B/op 8 allocs/op +BenchmarkGoJsonRest_Param5 500000 5509 ns/op 1097 B/op 16 allocs/op +BenchmarkGoRestful_Param5 200000 11232 ns/op 2392 B/op 21 allocs/op +BenchmarkGorillaMux_Param5 300000 7777 ns/op 1184 B/op 11 allocs/op +BenchmarkHttpRouter_Param5 3000000 631 ns/op 160 B/op 1 allocs/op +BenchmarkHttpTreeMux_Param5 1000000 2800 ns/op 576 B/op 6 allocs/op +BenchmarkKocha_Param5 1000000 2053 ns/op 440 B/op 10 allocs/op +BenchmarkLARS_Param5 10000000 232 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param5 500000 5888 ns/op 1056 B/op 10 allocs/op +BenchmarkMartini_Param5 200000 12807 ns/op 1232 B/op 11 allocs/op +BenchmarkPat_Param5 300000 7320 ns/op 964 B/op 32 allocs/op +BenchmarkPossum_Param5 1000000 2495 ns/op 560 B/op 6 allocs/op +BenchmarkR2router_Param5 1000000 1844 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Param5 2000000 935 ns/op 240 B/op 1 allocs/op +BenchmarkTango_Param5 1000000 2327 ns/op 360 B/op 8 allocs/op +BenchmarkTigerTonic_Param5 100000 18514 ns/op 2551 B/op 43 allocs/op +BenchmarkTraffic_Param5 200000 11997 ns/op 2248 B/op 25 allocs/op +BenchmarkVulcan_Param5 1000000 1333 ns/op 98 B/op 3 allocs/op +BenchmarkAce_Param20 1000000 2031 ns/op 640 B/op 1 allocs/op +BenchmarkBear_Param20 200000 7285 ns/op 1664 B/op 5 allocs/op +BenchmarkBeego_Param20 300000 6224 ns/op 368 B/op 4 allocs/op +BenchmarkBone_Param20 200000 8023 ns/op 1903 B/op 5 allocs/op +BenchmarkDenco_Param20 1000000 2262 ns/op 640 B/op 1 allocs/op +BenchmarkEcho_Param20 1000000 1387 ns/op 32 B/op 1 allocs/op +BenchmarkGin_Param20 3000000 503 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param20 100000 14408 ns/op 3795 B/op 15 allocs/op +BenchmarkGoji_Param20 500000 5272 ns/op 1247 B/op 2 allocs/op +BenchmarkGojiv2_Param20 1000000 4163 ns/op 1248 B/op 8 allocs/op +BenchmarkGoJsonRest_Param20 100000 17866 ns/op 4485 B/op 20 allocs/op +BenchmarkGoRestful_Param20 100000 21022 ns/op 4724 B/op 23 allocs/op +BenchmarkGorillaMux_Param20 100000 17055 ns/op 3547 B/op 13 allocs/op +BenchmarkHttpRouter_Param20 1000000 1748 ns/op 640 B/op 1 allocs/op +BenchmarkHttpTreeMux_Param20 200000 12246 ns/op 3196 B/op 10 allocs/op +BenchmarkKocha_Param20 300000 6861 ns/op 1808 B/op 27 allocs/op +BenchmarkLARS_Param20 3000000 526 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param20 100000 13069 ns/op 2906 B/op 12 allocs/op +BenchmarkMartini_Param20 100000 23602 ns/op 3597 B/op 13 allocs/op +BenchmarkPat_Param20 50000 32143 ns/op 4688 B/op 111 allocs/op +BenchmarkPossum_Param20 1000000 2396 ns/op 560 B/op 6 allocs/op +BenchmarkR2router_Param20 200000 8907 ns/op 2283 B/op 7 allocs/op +BenchmarkRivet_Param20 1000000 3280 ns/op 1024 B/op 1 allocs/op +BenchmarkTango_Param20 500000 4640 ns/op 856 B/op 8 allocs/op +BenchmarkTigerTonic_Param20 20000 67581 ns/op 10532 B/op 138 allocs/op +BenchmarkTraffic_Param20 50000 40313 ns/op 7941 B/op 45 allocs/op +BenchmarkVulcan_Param20 1000000 2264 ns/op 98 B/op 3 allocs/op +BenchmarkAce_ParamWrite 3000000 532 ns/op 40 B/op 2 allocs/op +BenchmarkBear_ParamWrite 1000000 1778 ns/op 456 B/op 5 allocs/op +BenchmarkBeego_ParamWrite 1000000 2596 ns/op 376 B/op 5 allocs/op +BenchmarkBone_ParamWrite 1000000 2519 ns/op 688 B/op 5 allocs/op +BenchmarkDenco_ParamWrite 5000000 411 ns/op 32 B/op 1 allocs/op +BenchmarkEcho_ParamWrite 2000000 718 ns/op 40 B/op 2 allocs/op +BenchmarkGin_ParamWrite 5000000 283 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParamWrite 1000000 2561 ns/op 656 B/op 9 allocs/op +BenchmarkGoji_ParamWrite 1000000 1378 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_ParamWrite 1000000 3128 ns/op 976 B/op 10 allocs/op +BenchmarkGoJsonRest_ParamWrite 500000 4446 ns/op 1128 B/op 18 allocs/op +BenchmarkGoRestful_ParamWrite 200000 10291 ns/op 2304 B/op 22 allocs/op +BenchmarkGorillaMux_ParamWrite 500000 5153 ns/op 1064 B/op 12 allocs/op +BenchmarkHttpRouter_ParamWrite 5000000 263 ns/op 32 B/op 1 allocs/op +BenchmarkHttpTreeMux_ParamWrite 1000000 1351 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_ParamWrite 3000000 538 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_ParamWrite 5000000 316 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParamWrite 500000 5756 ns/op 1160 B/op 14 allocs/op +BenchmarkMartini_ParamWrite 200000 13097 ns/op 1176 B/op 14 allocs/op +BenchmarkPat_ParamWrite 500000 4954 ns/op 1072 B/op 17 allocs/op +BenchmarkPossum_ParamWrite 1000000 2499 ns/op 560 B/op 6 allocs/op +BenchmarkR2router_ParamWrite 1000000 1531 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_ParamWrite 3000000 570 ns/op 112 B/op 2 allocs/op +BenchmarkTango_ParamWrite 2000000 957 ns/op 136 B/op 4 allocs/op +BenchmarkTigerTonic_ParamWrite 200000 7025 ns/op 1424 B/op 23 allocs/op +BenchmarkTraffic_ParamWrite 200000 10112 ns/op 2384 B/op 25 allocs/op +BenchmarkVulcan_ParamWrite 1000000 1006 ns/op 98 B/op 3 allocs/op +``` + +## GitHub + +``` +BenchmarkGin_GithubStatic 10000000 156 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_GithubStatic 5000000 294 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubStatic 2000000 893 ns/op 120 B/op 3 allocs/op +BenchmarkBeego_GithubStatic 1000000 2491 ns/op 368 B/op 4 allocs/op +BenchmarkBone_GithubStatic 50000 25300 ns/op 2880 B/op 60 allocs/op +BenchmarkDenco_GithubStatic 20000000 76.0 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_GithubStatic 2000000 516 ns/op 32 B/op 1 allocs/op +BenchmarkGocraftWeb_GithubStatic 1000000 1448 ns/op 296 B/op 5 allocs/op +BenchmarkGoji_GithubStatic 3000000 496 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_GithubStatic 1000000 2941 ns/op 928 B/op 7 allocs/op +BenchmarkGoRestful_GithubStatic 100000 27256 ns/op 3224 B/op 22 allocs/op +BenchmarkGoJsonRest_GithubStatic 1000000 2196 ns/op 329 B/op 11 allocs/op +BenchmarkGorillaMux_GithubStatic 50000 31617 ns/op 736 B/op 10 allocs/op +BenchmarkHttpRouter_GithubStatic 20000000 88.4 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GithubStatic 10000000 134 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_GithubStatic 20000000 113 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_GithubStatic 10000000 195 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubStatic 500000 3740 ns/op 768 B/op 9 allocs/op +BenchmarkMartini_GithubStatic 50000 27673 ns/op 768 B/op 9 allocs/op +BenchmarkPat_GithubStatic 100000 19470 ns/op 3648 B/op 76 allocs/op +BenchmarkPossum_GithubStatic 1000000 1729 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_GithubStatic 2000000 879 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_GithubStatic 10000000 231 ns/op 0 B/op 0 allocs/op +BenchmarkTango_GithubStatic 1000000 2325 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_GithubStatic 3000000 610 ns/op 48 B/op 1 allocs/op +BenchmarkTraffic_GithubStatic 20000 62973 ns/op 18904 B/op 148 allocs/op +BenchmarkVulcan_GithubStatic 1000000 1447 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GithubParam 2000000 686 ns/op 96 B/op 1 allocs/op +BenchmarkBear_GithubParam 1000000 2155 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_GithubParam 1000000 2713 ns/op 368 B/op 4 allocs/op +BenchmarkBone_GithubParam 100000 15088 ns/op 1760 B/op 18 allocs/op +BenchmarkDenco_GithubParam 2000000 629 ns/op 128 B/op 1 allocs/op +BenchmarkEcho_GithubParam 2000000 653 ns/op 32 B/op 1 allocs/op +BenchmarkGin_GithubParam 5000000 255 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubParam 1000000 3145 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_GithubParam 1000000 1916 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GithubParam 1000000 3975 ns/op 1024 B/op 10 allocs/op +BenchmarkGoJsonRest_GithubParam 300000 4134 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_GithubParam 50000 30782 ns/op 2360 B/op 21 allocs/op +BenchmarkGorillaMux_GithubParam 100000 17148 ns/op 1088 B/op 11 allocs/op +BenchmarkHttpRouter_GithubParam 3000000 523 ns/op 96 B/op 1 allocs/op +BenchmarkHttpTreeMux_GithubParam 1000000 1671 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_GithubParam 1000000 1021 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_GithubParam 5000000 283 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubParam 500000 4270 ns/op 1056 B/op 10 allocs/op +BenchmarkMartini_GithubParam 100000 21728 ns/op 1152 B/op 11 allocs/op +BenchmarkPat_GithubParam 200000 11208 ns/op 2464 B/op 48 allocs/op +BenchmarkPossum_GithubParam 1000000 2334 ns/op 560 B/op 6 allocs/op +BenchmarkR2router_GithubParam 1000000 1487 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GithubParam 2000000 782 ns/op 96 B/op 1 allocs/op +BenchmarkTango_GithubParam 1000000 2653 ns/op 344 B/op 8 allocs/op +BenchmarkTigerTonic_GithubParam 300000 14073 ns/op 1440 B/op 24 allocs/op +BenchmarkTraffic_GithubParam 50000 29164 ns/op 5992 B/op 52 allocs/op +BenchmarkVulcan_GithubParam 1000000 2529 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GithubAll 10000 134059 ns/op 13792 B/op 167 allocs/op +BenchmarkBear_GithubAll 5000 534445 ns/op 86448 B/op 943 allocs/op +BenchmarkBeego_GithubAll 3000 592444 ns/op 74705 B/op 812 allocs/op +BenchmarkBone_GithubAll 200 6957308 ns/op 698784 B/op 8453 allocs/op +BenchmarkDenco_GithubAll 10000 158819 ns/op 20224 B/op 167 allocs/op +BenchmarkEcho_GithubAll 10000 154700 ns/op 6496 B/op 203 allocs/op +BenchmarkGin_GithubAll 30000 48375 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubAll 3000 570806 ns/op 131656 B/op 1686 allocs/op +BenchmarkGoji_GithubAll 2000 818034 ns/op 56112 B/op 334 allocs/op +BenchmarkGojiv2_GithubAll 2000 1213973 ns/op 274768 B/op 3712 allocs/op +BenchmarkGoJsonRest_GithubAll 2000 785796 ns/op 134371 B/op 2737 allocs/op +BenchmarkGoRestful_GithubAll 300 5238188 ns/op 689672 B/op 4519 allocs/op +BenchmarkGorillaMux_GithubAll 100 10257726 ns/op 211840 B/op 2272 allocs/op +BenchmarkHttpRouter_GithubAll 20000 105414 ns/op 13792 B/op 167 allocs/op +BenchmarkHttpTreeMux_GithubAll 10000 319934 ns/op 65856 B/op 671 allocs/op +BenchmarkKocha_GithubAll 10000 209442 ns/op 23304 B/op 843 allocs/op +BenchmarkLARS_GithubAll 20000 62565 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubAll 2000 1161270 ns/op 204194 B/op 2000 allocs/op +BenchmarkMartini_GithubAll 200 9991713 ns/op 226549 B/op 2325 allocs/op +BenchmarkPat_GithubAll 200 5590793 ns/op 1499568 B/op 27435 allocs/op +BenchmarkPossum_GithubAll 10000 319768 ns/op 84448 B/op 609 allocs/op +BenchmarkR2router_GithubAll 10000 305134 ns/op 77328 B/op 979 allocs/op +BenchmarkRivet_GithubAll 10000 132134 ns/op 16272 B/op 167 allocs/op +BenchmarkTango_GithubAll 3000 552754 ns/op 63826 B/op 1618 allocs/op +BenchmarkTigerTonic_GithubAll 1000 1439483 ns/op 239104 B/op 5374 allocs/op +BenchmarkTraffic_GithubAll 100 11383067 ns/op 2659329 B/op 21848 allocs/op +BenchmarkVulcan_GithubAll 5000 394253 ns/op 19894 B/op 609 allocs/op +``` + +## Google+ + +``` +BenchmarkGin_GPlusStatic 10000000 183 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_GPlusStatic 5000000 276 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusStatic 2000000 652 ns/op 104 B/op 3 allocs/op +BenchmarkBeego_GPlusStatic 1000000 2239 ns/op 368 B/op 4 allocs/op +BenchmarkBone_GPlusStatic 5000000 380 ns/op 32 B/op 1 allocs/op +BenchmarkDenco_GPlusStatic 30000000 45.8 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_GPlusStatic 5000000 338 ns/op 32 B/op 1 allocs/op +BenchmarkGocraftWeb_GPlusStatic 1000000 1158 ns/op 280 B/op 5 allocs/op +BenchmarkGoji_GPlusStatic 5000000 331 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_GPlusStatic 1000000 2106 ns/op 928 B/op 7 allocs/op +BenchmarkGoJsonRest_GPlusStatic 1000000 1626 ns/op 329 B/op 11 allocs/op +BenchmarkGoRestful_GPlusStatic 300000 7598 ns/op 1976 B/op 20 allocs/op +BenchmarkGorillaMux_GPlusStatic 1000000 2629 ns/op 736 B/op 10 allocs/op +BenchmarkHttpRouter_GPlusStatic 30000000 52.5 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlusStatic 20000000 85.8 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_GPlusStatic 20000000 89.2 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_GPlusStatic 10000000 162 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusStatic 500000 3479 ns/op 768 B/op 9 allocs/op +BenchmarkMartini_GPlusStatic 200000 9092 ns/op 768 B/op 9 allocs/op +BenchmarkPat_GPlusStatic 3000000 493 ns/op 96 B/op 2 allocs/op +BenchmarkPossum_GPlusStatic 1000000 1467 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_GPlusStatic 2000000 788 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_GPlusStatic 20000000 114 ns/op 0 B/op 0 allocs/op +BenchmarkTango_GPlusStatic 1000000 1534 ns/op 200 B/op 8 allocs/op +BenchmarkTigerTonic_GPlusStatic 5000000 282 ns/op 32 B/op 1 allocs/op +BenchmarkTraffic_GPlusStatic 500000 3798 ns/op 1192 B/op 15 allocs/op +BenchmarkVulcan_GPlusStatic 2000000 1125 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GPlusParam 3000000 528 ns/op 64 B/op 1 allocs/op +BenchmarkBear_GPlusParam 1000000 1570 ns/op 480 B/op 5 allocs/op +BenchmarkBeego_GPlusParam 1000000 2369 ns/op 368 B/op 4 allocs/op +BenchmarkBone_GPlusParam 1000000 2028 ns/op 688 B/op 5 allocs/op +BenchmarkDenco_GPlusParam 5000000 385 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_GPlusParam 3000000 441 ns/op 32 B/op 1 allocs/op +BenchmarkGin_GPlusParam 10000000 174 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusParam 1000000 2033 ns/op 648 B/op 8 allocs/op +BenchmarkGoji_GPlusParam 1000000 1399 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GPlusParam 1000000 2641 ns/op 944 B/op 8 allocs/op +BenchmarkGoJsonRest_GPlusParam 1000000 2824 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_GPlusParam 200000 8875 ns/op 2296 B/op 21 allocs/op +BenchmarkGorillaMux_GPlusParam 200000 6291 ns/op 1056 B/op 11 allocs/op +BenchmarkHttpRouter_GPlusParam 5000000 316 ns/op 64 B/op 1 allocs/op +BenchmarkHttpTreeMux_GPlusParam 1000000 1129 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_GPlusParam 3000000 538 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_GPlusParam 10000000 198 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusParam 500000 3554 ns/op 1056 B/op 10 allocs/op +BenchmarkMartini_GPlusParam 200000 9831 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_GPlusParam 1000000 2706 ns/op 688 B/op 12 allocs/op +BenchmarkPossum_GPlusParam 1000000 2297 ns/op 560 B/op 6 allocs/op +BenchmarkR2router_GPlusParam 1000000 1318 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GPlusParam 5000000 399 ns/op 48 B/op 1 allocs/op +BenchmarkTango_GPlusParam 1000000 2070 ns/op 264 B/op 8 allocs/op +BenchmarkTigerTonic_GPlusParam 500000 4853 ns/op 1056 B/op 17 allocs/op +BenchmarkTraffic_GPlusParam 200000 8278 ns/op 1976 B/op 21 allocs/op +BenchmarkVulcan_GPlusParam 1000000 1243 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GPlus2Params 3000000 549 ns/op 64 B/op 1 allocs/op +BenchmarkBear_GPlus2Params 1000000 2112 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_GPlus2Params 500000 2750 ns/op 368 B/op 4 allocs/op +BenchmarkBone_GPlus2Params 300000 7032 ns/op 1040 B/op 9 allocs/op +BenchmarkDenco_GPlus2Params 3000000 502 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_GPlus2Params 3000000 641 ns/op 32 B/op 1 allocs/op +BenchmarkGin_GPlus2Params 5000000 250 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlus2Params 1000000 2681 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_GPlus2Params 1000000 1926 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GPlus2Params 500000 3996 ns/op 1024 B/op 11 allocs/op +BenchmarkGoJsonRest_GPlus2Params 500000 3886 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_GPlus2Params 200000 10376 ns/op 2360 B/op 21 allocs/op +BenchmarkGorillaMux_GPlus2Params 100000 14162 ns/op 1088 B/op 11 allocs/op +BenchmarkHttpRouter_GPlus2Params 5000000 336 ns/op 64 B/op 1 allocs/op +BenchmarkHttpTreeMux_GPlus2Params 1000000 1523 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_GPlus2Params 2000000 970 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_GPlus2Params 5000000 238 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlus2Params 500000 4016 ns/op 1056 B/op 10 allocs/op +BenchmarkMartini_GPlus2Params 100000 21253 ns/op 1200 B/op 13 allocs/op +BenchmarkPat_GPlus2Params 200000 8632 ns/op 2256 B/op 34 allocs/op +BenchmarkPossum_GPlus2Params 1000000 2171 ns/op 560 B/op 6 allocs/op +BenchmarkR2router_GPlus2Params 1000000 1340 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GPlus2Params 3000000 557 ns/op 96 B/op 1 allocs/op +BenchmarkTango_GPlus2Params 1000000 2186 ns/op 344 B/op 8 allocs/op +BenchmarkTigerTonic_GPlus2Params 200000 9060 ns/op 1488 B/op 24 allocs/op +BenchmarkTraffic_GPlus2Params 100000 20324 ns/op 3272 B/op 31 allocs/op +BenchmarkVulcan_GPlus2Params 1000000 2039 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GPlusAll 300000 6603 ns/op 640 B/op 11 allocs/op +BenchmarkBear_GPlusAll 100000 22363 ns/op 5488 B/op 61 allocs/op +BenchmarkBeego_GPlusAll 50000 38757 ns/op 4784 B/op 52 allocs/op +BenchmarkBone_GPlusAll 20000 54916 ns/op 10336 B/op 98 allocs/op +BenchmarkDenco_GPlusAll 300000 4959 ns/op 672 B/op 11 allocs/op +BenchmarkEcho_GPlusAll 200000 6558 ns/op 416 B/op 13 allocs/op +BenchmarkGin_GPlusAll 500000 2757 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusAll 50000 34615 ns/op 8040 B/op 103 allocs/op +BenchmarkGoji_GPlusAll 100000 16002 ns/op 3696 B/op 22 allocs/op +BenchmarkGojiv2_GPlusAll 50000 35060 ns/op 12624 B/op 115 allocs/op +BenchmarkGoJsonRest_GPlusAll 50000 41479 ns/op 8117 B/op 170 allocs/op +BenchmarkGoRestful_GPlusAll 10000 131653 ns/op 32024 B/op 275 allocs/op +BenchmarkGorillaMux_GPlusAll 10000 101380 ns/op 13296 B/op 142 allocs/op +BenchmarkHttpRouter_GPlusAll 500000 3711 ns/op 640 B/op 11 allocs/op +BenchmarkHttpTreeMux_GPlusAll 100000 14438 ns/op 4032 B/op 38 allocs/op +BenchmarkKocha_GPlusAll 200000 8039 ns/op 976 B/op 43 allocs/op +BenchmarkLARS_GPlusAll 500000 2630 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusAll 30000 51123 ns/op 13152 B/op 128 allocs/op +BenchmarkMartini_GPlusAll 10000 176157 ns/op 14016 B/op 145 allocs/op +BenchmarkPat_GPlusAll 20000 69911 ns/op 16576 B/op 298 allocs/op +BenchmarkPossum_GPlusAll 100000 20716 ns/op 5408 B/op 39 allocs/op +BenchmarkR2router_GPlusAll 100000 17463 ns/op 5040 B/op 63 allocs/op +BenchmarkRivet_GPlusAll 300000 5142 ns/op 768 B/op 11 allocs/op +BenchmarkTango_GPlusAll 50000 27321 ns/op 3656 B/op 104 allocs/op +BenchmarkTigerTonic_GPlusAll 20000 77597 ns/op 14512 B/op 288 allocs/op +BenchmarkTraffic_GPlusAll 10000 151406 ns/op 37360 B/op 392 allocs/op +BenchmarkVulcan_GPlusAll 100000 18555 ns/op 1274 B/op 39 allocs/op +``` + +## Parse.com + +``` +BenchmarkGin_ParseStatic 10000000 133 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_ParseStatic 5000000 241 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseStatic 2000000 728 ns/op 120 B/op 3 allocs/op +BenchmarkBeego_ParseStatic 1000000 2623 ns/op 368 B/op 4 allocs/op +BenchmarkBone_ParseStatic 1000000 1285 ns/op 144 B/op 3 allocs/op +BenchmarkDenco_ParseStatic 30000000 57.8 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_ParseStatic 5000000 342 ns/op 32 B/op 1 allocs/op +BenchmarkGocraftWeb_ParseStatic 1000000 1478 ns/op 296 B/op 5 allocs/op +BenchmarkGoji_ParseStatic 3000000 415 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_ParseStatic 1000000 2087 ns/op 928 B/op 7 allocs/op +BenchmarkGoJsonRest_ParseStatic 1000000 1712 ns/op 329 B/op 11 allocs/op +BenchmarkGoRestful_ParseStatic 200000 11072 ns/op 3224 B/op 22 allocs/op +BenchmarkGorillaMux_ParseStatic 500000 4129 ns/op 752 B/op 11 allocs/op +BenchmarkHttpRouter_ParseStatic 30000000 52.4 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParseStatic 20000000 109 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_ParseStatic 20000000 81.8 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_ParseStatic 10000000 150 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseStatic 1000000 3288 ns/op 768 B/op 9 allocs/op +BenchmarkMartini_ParseStatic 200000 9110 ns/op 768 B/op 9 allocs/op +BenchmarkPat_ParseStatic 1000000 1135 ns/op 240 B/op 5 allocs/op +BenchmarkPossum_ParseStatic 1000000 1557 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_ParseStatic 2000000 730 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_ParseStatic 10000000 121 ns/op 0 B/op 0 allocs/op +BenchmarkTango_ParseStatic 1000000 1688 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_ParseStatic 3000000 427 ns/op 48 B/op 1 allocs/op +BenchmarkTraffic_ParseStatic 500000 5962 ns/op 1816 B/op 20 allocs/op +BenchmarkVulcan_ParseStatic 2000000 969 ns/op 98 B/op 3 allocs/op +BenchmarkAce_ParseParam 3000000 497 ns/op 64 B/op 1 allocs/op +BenchmarkBear_ParseParam 1000000 1473 ns/op 467 B/op 5 allocs/op +BenchmarkBeego_ParseParam 1000000 2384 ns/op 368 B/op 4 allocs/op +BenchmarkBone_ParseParam 1000000 2513 ns/op 768 B/op 6 allocs/op +BenchmarkDenco_ParseParam 5000000 364 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_ParseParam 5000000 418 ns/op 32 B/op 1 allocs/op +BenchmarkGin_ParseParam 10000000 163 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseParam 1000000 2361 ns/op 664 B/op 8 allocs/op +BenchmarkGoji_ParseParam 1000000 1590 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_ParseParam 1000000 2851 ns/op 976 B/op 9 allocs/op +BenchmarkGoJsonRest_ParseParam 1000000 2965 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_ParseParam 200000 12207 ns/op 3544 B/op 23 allocs/op +BenchmarkGorillaMux_ParseParam 500000 5187 ns/op 1088 B/op 12 allocs/op +BenchmarkHttpRouter_ParseParam 5000000 275 ns/op 64 B/op 1 allocs/op +BenchmarkHttpTreeMux_ParseParam 1000000 1108 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_ParseParam 3000000 495 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_ParseParam 10000000 192 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseParam 500000 4103 ns/op 1056 B/op 10 allocs/op +BenchmarkMartini_ParseParam 200000 9878 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_ParseParam 500000 3657 ns/op 1120 B/op 17 allocs/op +BenchmarkPossum_ParseParam 1000000 2084 ns/op 560 B/op 6 allocs/op +BenchmarkR2router_ParseParam 1000000 1251 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_ParseParam 5000000 335 ns/op 48 B/op 1 allocs/op +BenchmarkTango_ParseParam 1000000 1854 ns/op 280 B/op 8 allocs/op +BenchmarkTigerTonic_ParseParam 500000 4582 ns/op 1008 B/op 17 allocs/op +BenchmarkTraffic_ParseParam 200000 8125 ns/op 2248 B/op 23 allocs/op +BenchmarkVulcan_ParseParam 1000000 1148 ns/op 98 B/op 3 allocs/op +BenchmarkAce_Parse2Params 3000000 539 ns/op 64 B/op 1 allocs/op +BenchmarkBear_Parse2Params 1000000 1778 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_Parse2Params 1000000 2519 ns/op 368 B/op 4 allocs/op +BenchmarkBone_Parse2Params 1000000 2596 ns/op 720 B/op 5 allocs/op +BenchmarkDenco_Parse2Params 3000000 492 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_Parse2Params 3000000 484 ns/op 32 B/op 1 allocs/op +BenchmarkGin_Parse2Params 10000000 193 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Parse2Params 1000000 2575 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_Parse2Params 1000000 1373 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Parse2Params 500000 2416 ns/op 960 B/op 8 allocs/op +BenchmarkGoJsonRest_Parse2Params 300000 3452 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_Parse2Params 100000 17719 ns/op 6008 B/op 25 allocs/op +BenchmarkGorillaMux_Parse2Params 300000 5102 ns/op 1088 B/op 11 allocs/op +BenchmarkHttpRouter_Parse2Params 5000000 303 ns/op 64 B/op 1 allocs/op +BenchmarkHttpTreeMux_Parse2Params 1000000 1372 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_Parse2Params 2000000 874 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_Parse2Params 10000000 192 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Parse2Params 500000 3871 ns/op 1056 B/op 10 allocs/op +BenchmarkMartini_Parse2Params 200000 9954 ns/op 1152 B/op 11 allocs/op +BenchmarkPat_Parse2Params 500000 4194 ns/op 832 B/op 17 allocs/op +BenchmarkPossum_Parse2Params 1000000 2121 ns/op 560 B/op 6 allocs/op +BenchmarkR2router_Parse2Params 1000000 1415 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Parse2Params 3000000 457 ns/op 96 B/op 1 allocs/op +BenchmarkTango_Parse2Params 1000000 1914 ns/op 312 B/op 8 allocs/op +BenchmarkTigerTonic_Parse2Params 300000 6895 ns/op 1408 B/op 24 allocs/op +BenchmarkTraffic_Parse2Params 200000 8317 ns/op 2040 B/op 22 allocs/op +BenchmarkVulcan_Parse2Params 1000000 1274 ns/op 98 B/op 3 allocs/op +BenchmarkAce_ParseAll 200000 10401 ns/op 640 B/op 16 allocs/op +BenchmarkBear_ParseAll 50000 37743 ns/op 8928 B/op 110 allocs/op +BenchmarkBeego_ParseAll 20000 63193 ns/op 9568 B/op 104 allocs/op +BenchmarkBone_ParseAll 20000 61767 ns/op 14160 B/op 131 allocs/op +BenchmarkDenco_ParseAll 300000 7036 ns/op 928 B/op 16 allocs/op +BenchmarkEcho_ParseAll 200000 11824 ns/op 832 B/op 26 allocs/op +BenchmarkGin_ParseAll 300000 4199 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseAll 30000 51758 ns/op 13728 B/op 181 allocs/op +BenchmarkGoji_ParseAll 50000 29614 ns/op 5376 B/op 32 allocs/op +BenchmarkGojiv2_ParseAll 20000 68676 ns/op 24464 B/op 199 allocs/op +BenchmarkGoJsonRest_ParseAll 20000 76135 ns/op 13866 B/op 321 allocs/op +BenchmarkGoRestful_ParseAll 5000 389487 ns/op 110928 B/op 600 allocs/op +BenchmarkGorillaMux_ParseAll 10000 221250 ns/op 24864 B/op 292 allocs/op +BenchmarkHttpRouter_ParseAll 200000 6444 ns/op 640 B/op 16 allocs/op +BenchmarkHttpTreeMux_ParseAll 50000 30702 ns/op 5728 B/op 51 allocs/op +BenchmarkKocha_ParseAll 200000 13712 ns/op 1112 B/op 54 allocs/op +BenchmarkLARS_ParseAll 300000 6925 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseAll 20000 96278 ns/op 24576 B/op 250 allocs/op +BenchmarkMartini_ParseAll 5000 271352 ns/op 25072 B/op 253 allocs/op +BenchmarkPat_ParseAll 20000 74941 ns/op 17264 B/op 343 allocs/op +BenchmarkPossum_ParseAll 50000 39947 ns/op 10816 B/op 78 allocs/op +BenchmarkR2router_ParseAll 50000 42479 ns/op 8352 B/op 120 allocs/op +BenchmarkRivet_ParseAll 200000 7726 ns/op 912 B/op 16 allocs/op +BenchmarkTango_ParseAll 30000 50014 ns/op 7168 B/op 208 allocs/op +BenchmarkTigerTonic_ParseAll 10000 106550 ns/op 19728 B/op 379 allocs/op +BenchmarkTraffic_ParseAll 10000 216037 ns/op 57776 B/op 642 allocs/op +BenchmarkVulcan_ParseAll 50000 34379 ns/op 2548 B/op 78 allocs/op ``` diff --git a/README.md b/README.md index 76ab3b01..07fc960e 100644 --- a/README.md +++ b/README.md @@ -44,41 +44,40 @@ Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httpr [See all benchmarks](/BENCHMARKS.md) +Benchmark name | (1) | (2) | (3) | (4) +--------------------------------------------|-----------:|------------:|-----------:|---------: +**BenchmarkGin_GithubAll** | **30000** | **48375** | **0** | **0** +BenchmarkAce_GithubAll | 10000 | 134059 | 13792 | 167 +BenchmarkBear_GithubAll | 5000 | 534445 | 86448 | 943 +BenchmarkBeego_GithubAll | 3000 | 592444 | 74705 | 812 +BenchmarkBone_GithubAll | 200 | 6957308 | 698784 | 8453 +BenchmarkDenco_GithubAll | 10000 | 158819 | 20224 | 167 +BenchmarkEcho_GithubAll | 10000 | 154700 | 6496 | 203 +BenchmarkGocraftWeb_GithubAll | 3000 | 570806 | 131656 | 1686 +BenchmarkGoji_GithubAll | 2000 | 818034 | 56112 | 334 +BenchmarkGojiv2_GithubAll | 2000 | 1213973 | 274768 | 3712 +BenchmarkGoJsonRest_GithubAll | 2000 | 785796 | 134371 | 2737 +BenchmarkGoRestful_GithubAll | 300 | 5238188 | 689672 | 4519 +BenchmarkGorillaMux_GithubAll | 100 | 10257726 | 211840 | 2272 +BenchmarkHttpRouter_GithubAll | 20000 | 105414 | 13792 | 167 +BenchmarkHttpTreeMux_GithubAll | 10000 | 319934 | 65856 | 671 +BenchmarkKocha_GithubAll | 10000 | 209442 | 23304 | 843 +BenchmarkLARS_GithubAll | 20000 | 62565 | 0 | 0 +BenchmarkMacaron_GithubAll | 2000 | 1161270 | 204194 | 2000 +BenchmarkMartini_GithubAll | 200 | 9991713 | 226549 | 2325 +BenchmarkPat_GithubAll | 200 | 5590793 | 1499568 | 27435 +BenchmarkPossum_GithubAll | 10000 | 319768 | 84448 | 609 +BenchmarkR2router_GithubAll | 10000 | 305134 | 77328 | 979 +BenchmarkRivet_GithubAll | 10000 | 132134 | 16272 | 167 +BenchmarkTango_GithubAll | 3000 | 552754 | 63826 | 1618 +BenchmarkTigerTonic_GithubAll | 1000 | 1439483 | 239104 | 5374 +BenchmarkTraffic_GithubAll | 100 | 11383067 | 2659329 | 21848 +BenchmarkVulcan_GithubAll | 5000 | 394253 | 19894 | 609 -Benchmark name | (1) | (2) | (3) | (4) ---------------------------------|----------:|----------:|----------:|------: -BenchmarkAce_GithubAll | 10000 | 109482 | 13792 | 167 -BenchmarkBear_GithubAll | 10000 | 287490 | 79952 | 943 -BenchmarkBeego_GithubAll | 3000 | 562184 | 146272 | 2092 -BenchmarkBone_GithubAll | 500 | 2578716 | 648016 | 8119 -BenchmarkDenco_GithubAll | 20000 | 94955 | 20224 | 167 -BenchmarkEcho_GithubAll | 30000 | 58705 | 0 | 0 -**BenchmarkGin_GithubAll** | **30000** | **50991** | **0** | **0** -BenchmarkGocraftWeb_GithubAll | 5000 | 449648 | 133280 | 1889 -BenchmarkGoji_GithubAll | 2000 | 689748 | 56113 | 334 -BenchmarkGoJsonRest_GithubAll | 5000 | 537769 | 135995 | 2940 -BenchmarkGoRestful_GithubAll | 100 | 18410628 | 797236 | 7725 -BenchmarkGorillaMux_GithubAll | 200 | 8036360 | 153137 | 1791 -BenchmarkHttpRouter_GithubAll | 20000 | 63506 | 13792 | 167 -BenchmarkHttpTreeMux_GithubAll | 10000 | 165927 | 56112 | 334 -BenchmarkKocha_GithubAll | 10000 | 171362 | 23304 | 843 -BenchmarkMacaron_GithubAll | 2000 | 817008 | 224960 | 2315 -BenchmarkMartini_GithubAll | 100 | 12609209 | 237952 | 2686 -BenchmarkPat_GithubAll | 300 | 4830398 | 1504101 | 32222 -BenchmarkPossum_GithubAll | 10000 | 301716 | 97440 | 812 -BenchmarkR2router_GithubAll | 10000 | 270691 | 77328 | 1182 -BenchmarkRevel_GithubAll | 1000 | 1491919 | 345553 | 5918 -BenchmarkRivet_GithubAll | 10000 | 283860 | 84272 | 1079 -BenchmarkTango_GithubAll | 5000 | 473821 | 87078 | 2470 -BenchmarkTigerTonic_GithubAll | 2000 | 1120131 | 241088 | 6052 -BenchmarkTraffic_GithubAll | 200 | 8708979 | 2664762 | 22390 -BenchmarkVulcan_GithubAll | 5000 | 353392 | 19894 | 609 -BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648 - -(1): Total Repetitions -(2): Single Repetition Duration (ns/op) -(3): Heap Memory (B/op) -(4): Average Allocations per Repetition (allocs/op) +(1): Total Repetitions achieved in constant time, higher means more confident result +(2): Single Repetition Duration (ns/op), lower is better +(3): Heap Memory (B/op), lower is better +(4): Average Allocations per Repetition (allocs/op), lower is better ## Gin v1. stable From c19aa0598b6bab47f498e68ebc60ceafded8631f Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Wed, 19 Jul 2017 15:50:05 +0800 Subject: [PATCH 071/113] feat(context): add BindQuery func (#1029) * feat(context): add BindQuery func, only parse/bind the query string params. * docs(readme): add BindQuery section. * docs(readme): fix import. * docs(readme): separate import --- README.md | 38 +++++++++++++++++++++++++++++++++++++- binding/binding.go | 1 + binding/binding_test.go | 27 +++++++++++++++++++++++++++ binding/query.go | 23 +++++++++++++++++++++++ context.go | 5 +++++ context_test.go | 16 ++++++++++++++++ 6 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 binding/query.go diff --git a/README.md b/README.md index 07fc960e..35858737 100644 --- a/README.md +++ b/README.md @@ -460,7 +460,43 @@ func main() { } ``` -### Bind Query String +### Only Bind Query String + +`BindQuery` 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" + + "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.BindQuery(&person) == nil { + log.Println("====== Only Bind By Query String ======") + log.Println(person.Name) + log.Println(person.Address) + } + c.String(200, "Success") +} + +``` + +### Bind Query String or Post Data See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292). diff --git a/binding/binding.go b/binding/binding.go index 1dbf2460..971547c2 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -39,6 +39,7 @@ var ( JSON = jsonBinding{} XML = xmlBinding{} Form = formBinding{} + Query = queryBinding{} FormPost = formPostBinding{} FormMultipart = formMultipartBinding{} ProtoBuf = protobufBinding{} diff --git a/binding/binding_test.go b/binding/binding_test.go index d7cdf77a..5575e166 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -67,6 +67,18 @@ func TestBindingForm2(t *testing.T) { "", "") } +func TestBindingQuery(t *testing.T) { + testQueryBinding(t, "POST", + "/?foo=bar&bar=foo", "/", + "foo=unused", "bar2=foo") +} + +func TestBindingQuery2(t *testing.T) { + testQueryBinding(t, "GET", + "/?foo=bar&bar=foo", "/?bar2=foo", + "foo=unused", "") +} + func TestBindingXML(t *testing.T) { testBodyBinding(t, XML, "xml", @@ -204,6 +216,21 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) assert.Error(t, err) } +func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string) { + b := Query + assert.Equal(t, b.Name(), "query") + + obj := FooBarStruct{} + req := requestWithBody(method, path, body) + if method == "POST" { + req.Header.Add("Content-Type", MIMEPOSTForm) + } + err := b.Bind(req, &obj) + assert.NoError(t, err) + assert.Equal(t, obj.Foo, "bar") + assert.Equal(t, obj.Bar, "foo") +} + func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { assert.Equal(t, b.Name(), name) diff --git a/binding/query.go b/binding/query.go new file mode 100644 index 00000000..a789f798 --- /dev/null +++ b/binding/query.go @@ -0,0 +1,23 @@ +// 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. + +package binding + +import ( + "net/http" +) + +type queryBinding struct{} + +func (queryBinding) Name() string { + return "query" +} + +func (queryBinding) Bind(req *http.Request, obj interface{}) error { + values := req.URL.Query() + if err := mapForm(obj, values); err != nil { + return err + } + return validate(obj) +} diff --git a/context.go b/context.go index 306e2055..87aa1bea 100644 --- a/context.go +++ b/context.go @@ -467,6 +467,11 @@ func (c *Context) BindJSON(obj interface{}) error { return c.MustBindWith(obj, binding.JSON) } +// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query) +func (c *Context) BindQuery(obj interface{}) error { + return c.MustBindWith(obj, binding.Query) +} + // MustBindWith binds the passed struct pointer using the specified binding // engine. It will abort the request with HTTP 400 if any error ocurrs. // See the binding package. diff --git a/context_test.go b/context_test.go index db960fb3..15569bf2 100644 --- a/context_test.go +++ b/context_test.go @@ -1186,6 +1186,22 @@ func TestContextBindWithJSON(t *testing.T) { assert.Equal(t, w.Body.Len(), 0) } +func TestContextBindWithQuery(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) + + var obj struct { + Foo string `form:"foo"` + Bar string `form:"bar"` + } + assert.NoError(t, c.BindQuery(&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 7b508186dd71146cbdb3d14f2c2e1aaaa51f7e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Wed, 19 Jul 2017 20:49:18 +0800 Subject: [PATCH 072/113] style: remove optional return (#1036) --- auth.go | 1 - gin.go | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/auth.go b/auth.go index 75a7c892..c214e213 100644 --- a/auth.go +++ b/auth.go @@ -58,7 +58,6 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc { // The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using // c.MustGet(gin.AuthUserKey) c.Set(AuthUserKey, user) - return } } diff --git a/gin.go b/gin.go index fc9214dc..0372931d 100644 --- a/gin.go +++ b/gin.go @@ -158,9 +158,9 @@ func (engine *Engine) LoadHTMLGlob(pattern string) { engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims} return } + templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)) engine.SetHTMLTemplate(templ) - return } func (engine *Engine) LoadHTMLFiles(files ...string) { @@ -168,9 +168,9 @@ func (engine *Engine) LoadHTMLFiles(files ...string) { engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims} return } + templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...)) engine.SetHTMLTemplate(templ) - return } func (engine *Engine) SetHTMLTemplate(templ *template.Template) { From d39ed41ab3795346f5f843ec31bc0138be07eaab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Fri, 21 Jul 2017 10:29:23 +0800 Subject: [PATCH 073/113] update template example (#1038) * update template example * add template example folder --- README.md | 55 +++++++++++++++++++++++---------------- examples/template/main.go | 32 +++++++++++++++++++++++ 2 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 examples/template/main.go diff --git a/README.md b/README.md index 35858737..b14c2a2a 100644 --- a/README.md +++ b/README.md @@ -791,31 +791,42 @@ You may use custom delims #### Custom Template Funcs +See the detail [example code](examples/template). + main.go ```go - ... - - func formatAsDate(t time.Time) string { - year, month, day := t.Date() - return fmt.Sprintf("%d/%02d/%02d", year, month, day) - } - - ... - - router.SetFuncMap(template.FuncMap{ - "formatAsDate": formatAsDate, - }) - - ... - - 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), - }) - }) - - ... +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("./fixtures/basic/raw.tmpl") + + router.GET("/raw", func(c *gin.Context) { + c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), + }) + }) + + router.Run(":8080") +} + ``` raw.tmpl diff --git a/examples/template/main.go b/examples/template/main.go new file mode 100644 index 00000000..f9e611df --- /dev/null +++ b/examples/template/main.go @@ -0,0 +1,32 @@ +package main + +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("../../fixtures/basic/raw.tmpl") + + router.GET("/raw", func(c *gin.Context) { + c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), + }) + }) + + router.Run(":8080") +} From 7fafb3f4a177ca92821dc5bf6e59f17e5f130588 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Fri, 28 Jul 2017 08:50:58 +0800 Subject: [PATCH 074/113] doc(context): add status func comment. (#1042) --- context.go | 1 + 1 file changed, 1 insertion(+) diff --git a/context.go b/context.go index 87aa1bea..35ca8d2e 100644 --- a/context.go +++ b/context.go @@ -561,6 +561,7 @@ func bodyAllowedForStatus(status int) bool { return true } +// Status sets the HTTP response code. func (c *Context) Status(code int) { c.writermem.WriteHeader(code) } From df37e74fa1bbf8897ec2fa6ab347b27acdf5c444 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Tue, 1 Aug 2017 12:49:28 +0800 Subject: [PATCH 075/113] doc(context): more clearer bind doc when input is not valid (#1049) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 35ca8d2e..895ba7a1 100644 --- a/context.go +++ b/context.go @@ -456,7 +456,7 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error // 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 ParseBody() but this method also writes a 400 error if the json is not valid. +// It will 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 { b := binding.Default(c.Request.Method, c.ContentType()) return c.MustBindWith(obj, b) From 4b54b862720f68d1ee16e89384e370b04e78f659 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Wed, 2 Aug 2017 23:00:10 +0800 Subject: [PATCH 076/113] fix composite literal uses unkeyed fields warnings, #1050 (#1051) --- gin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gin.go b/gin.go index 0372931d..8347ce22 100644 --- a/gin.go +++ b/gin.go @@ -121,7 +121,7 @@ func New() *Engine { UseRawPath: false, UnescapePathValues: true, trees: make(methodTrees, 0, 9), - delims: render.Delims{"{{", "}}"}, + delims: render.Delims{Left: "{{", Right: "}}"}, secureJsonPrefix: "while(1);", } engine.RouterGroup.engine = engine @@ -143,7 +143,7 @@ func (engine *Engine) allocateContext() *Context { } func (engine *Engine) Delims(left, right string) *Engine { - engine.delims = render.Delims{left, right} + engine.delims = render.Delims{Left: left, Right: right} return engine } From 81007d2ce0176f7a9ce52dd12e56edd7ef40e72c Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Fri, 4 Aug 2017 13:45:59 +0800 Subject: [PATCH 077/113] refactor(test): unify assert.Equal usage (#1054) --- auth_test.go | 20 ++++++++++---------- debug_test.go | 6 +++--- errors_test.go | 24 ++++++++++++------------ middleware_test.go | 28 ++++++++++++++-------------- response_writer_test.go | 36 ++++++++++++++++++------------------ 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/auth_test.go b/auth_test.go index b22d9ced..2f1ae70e 100644 --- a/auth_test.go +++ b/auth_test.go @@ -53,15 +53,15 @@ func TestBasicAuthSearchCredential(t *testing.T) { }) user, found := pairs.searchCredential(authorizationHeader("admin", "password")) - assert.Equal(t, user, "admin") + assert.Equal(t, "admin", user) assert.True(t, found) user, found = pairs.searchCredential(authorizationHeader("foo", "bar")) - assert.Equal(t, user, "foo") + assert.Equal(t, "foo", user) assert.True(t, found) user, found = pairs.searchCredential(authorizationHeader("bar", "foo")) - assert.Equal(t, user, "bar") + assert.Equal(t, "bar", user) assert.True(t, found) user, found = pairs.searchCredential(authorizationHeader("admins", "password")) @@ -78,7 +78,7 @@ func TestBasicAuthSearchCredential(t *testing.T) { } func TestBasicAuthAuthorizationHeader(t *testing.T) { - assert.Equal(t, authorizationHeader("admin", "password"), "Basic YWRtaW46cGFzc3dvcmQ=") + assert.Equal(t, "Basic YWRtaW46cGFzc3dvcmQ=", authorizationHeader("admin", "password")) } func TestBasicAuthSecureCompare(t *testing.T) { @@ -101,8 +101,8 @@ func TestBasicAuthSucceed(t *testing.T) { req.Header.Set("Authorization", authorizationHeader("admin", "password")) router.ServeHTTP(w, req) - assert.Equal(t, w.Code, 200) - assert.Equal(t, w.Body.String(), "admin") + assert.Equal(t, 200, w.Code) + assert.Equal(t, "admin", w.Body.String()) } func TestBasicAuth401(t *testing.T) { @@ -121,8 +121,8 @@ func TestBasicAuth401(t *testing.T) { router.ServeHTTP(w, req) assert.False(t, called) - assert.Equal(t, w.Code, 401) - assert.Equal(t, w.HeaderMap.Get("WWW-Authenticate"), "Basic realm=\"Authorization Required\"") + assert.Equal(t, 401, w.Code) + assert.Equal(t, "Basic realm=\"Authorization Required\"", w.HeaderMap.Get("WWW-Authenticate")) } func TestBasicAuth401WithCustomRealm(t *testing.T) { @@ -141,6 +141,6 @@ func TestBasicAuth401WithCustomRealm(t *testing.T) { router.ServeHTTP(w, req) assert.False(t, called) - assert.Equal(t, w.Code, 401) - assert.Equal(t, w.HeaderMap.Get("WWW-Authenticate"), "Basic realm=\"My Custom \\\"Realm\\\"\"") + assert.Equal(t, 401, w.Code) + assert.Equal(t, "Basic realm=\"My Custom \\\"Realm\\\"\"", w.HeaderMap.Get("WWW-Authenticate")) } diff --git a/debug_test.go b/debug_test.go index 2b010858..366d4613 100644 --- a/debug_test.go +++ b/debug_test.go @@ -42,7 +42,7 @@ func TestDebugPrint(t *testing.T) { SetMode(DebugMode) debugPrint("these are %d %s\n", 2, "error messages") - assert.Equal(t, w.String(), "[GIN-debug] these are 2 error messages\n") + assert.Equal(t, "[GIN-debug] these are 2 error messages\n", w.String()) } func TestDebugPrintError(t *testing.T) { @@ -55,7 +55,7 @@ func TestDebugPrintError(t *testing.T) { assert.Empty(t, w.String()) debugPrintError(errors.New("this is an error")) - assert.Equal(t, w.String(), "[GIN-debug] [ERROR] this is an error\n") + assert.Equal(t, "[GIN-debug] [ERROR] this is an error\n", w.String()) } func TestDebugPrintRoutes(t *testing.T) { @@ -83,7 +83,7 @@ func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) { defer teardown() debugPrintWARNINGSetHTMLTemplate() - assert.Equal(t, w.String(), "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n") + assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", w.String()) } func setup(w io.Writer) { diff --git a/errors_test.go b/errors_test.go index 5e596aff..a666d7c1 100644 --- a/errors_test.go +++ b/errors_test.go @@ -60,7 +60,7 @@ func TestError(t *testing.T) { data string } err.SetMeta(customError{status: "200", data: "other data"}) - assert.Equal(t, err.JSON(), customError{status: "200", data: "other data"}) + assert.Equal(t, customError{status: "200", data: "other data"}, err.JSON()) } func TestErrorSlice(t *testing.T) { @@ -71,33 +71,33 @@ func TestErrorSlice(t *testing.T) { } assert.Equal(t, errs, errs.ByType(ErrorTypeAny)) - assert.Equal(t, errs.Last().Error(), "third") - assert.Equal(t, errs.Errors(), []string{"first", "second", "third"}) - assert.Equal(t, errs.ByType(ErrorTypePublic).Errors(), []string{"third"}) - assert.Equal(t, errs.ByType(ErrorTypePrivate).Errors(), []string{"first", "second"}) - assert.Equal(t, errs.ByType(ErrorTypePublic|ErrorTypePrivate).Errors(), []string{"first", "second", "third"}) + assert.Equal(t, "third", errs.Last().Error()) + assert.Equal(t, []string{"first", "second", "third"}, errs.Errors()) + assert.Equal(t, []string{"third"}, errs.ByType(ErrorTypePublic).Errors()) + assert.Equal(t, []string{"first", "second"}, errs.ByType(ErrorTypePrivate).Errors()) + assert.Equal(t, []string{"first", "second", "third"}, errs.ByType(ErrorTypePublic|ErrorTypePrivate).Errors()) assert.Empty(t, errs.ByType(ErrorTypeBind)) assert.Empty(t, errs.ByType(ErrorTypeBind).String()) - assert.Equal(t, errs.String(), `Error #01: first + assert.Equal(t, `Error #01: first Error #02: second Meta: some data Error #03: third Meta: map[status:400] -`) - assert.Equal(t, errs.JSON(), []interface{}{ +`, errs.String()) + assert.Equal(t, []interface{}{ H{"error": "first"}, H{"error": "second", "meta": "some data"}, H{"error": "third", "status": "400"}, - }) + }, errs.JSON()) jsonBytes, _ := json.Marshal(errs) assert.Equal(t, "[{\"error\":\"first\"},{\"error\":\"second\",\"meta\":\"some data\"},{\"error\":\"third\",\"status\":\"400\"}]", string(jsonBytes)) errs = errorMsgs{ {Err: errors.New("first"), Type: ErrorTypePrivate}, } - assert.Equal(t, errs.JSON(), H{"error": "first"}) + assert.Equal(t, H{"error": "first"}, errs.JSON()) jsonBytes, _ = json.Marshal(errs) - assert.Equal(t, string(jsonBytes), "{\"error\":\"first\"}") + assert.Equal(t, "{\"error\":\"first\"}", string(jsonBytes)) errs = errorMsgs{} assert.Nil(t, errs.Last()) diff --git a/middleware_test.go b/middleware_test.go index 5572e790..aa6a37a8 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -37,8 +37,8 @@ func TestMiddlewareGeneralCase(t *testing.T) { w := performRequest(router, "GET", "/") // TEST - assert.Equal(t, w.Code, 200) - assert.Equal(t, signature, "ACDB") + assert.Equal(t, 200, w.Code) + assert.Equal(t, "ACDB", signature) } func TestMiddlewareNoRoute(t *testing.T) { @@ -73,8 +73,8 @@ func TestMiddlewareNoRoute(t *testing.T) { w := performRequest(router, "GET", "/") // TEST - assert.Equal(t, w.Code, 404) - assert.Equal(t, signature, "ACEGHFDB") + assert.Equal(t, 404, w.Code) + assert.Equal(t, "ACEGHFDB", signature) } func TestMiddlewareNoMethodEnabled(t *testing.T) { @@ -110,8 +110,8 @@ func TestMiddlewareNoMethodEnabled(t *testing.T) { w := performRequest(router, "GET", "/") // TEST - assert.Equal(t, w.Code, 405) - assert.Equal(t, signature, "ACEGHFDB") + assert.Equal(t, 405, w.Code) + assert.Equal(t, "ACEGHFDB", signature) } func TestMiddlewareNoMethodDisabled(t *testing.T) { @@ -147,8 +147,8 @@ func TestMiddlewareNoMethodDisabled(t *testing.T) { w := performRequest(router, "GET", "/") // TEST - assert.Equal(t, w.Code, 404) - assert.Equal(t, signature, "AC X DB") + assert.Equal(t, 404, w.Code) + assert.Equal(t, "AC X DB", signature) } func TestMiddlewareAbort(t *testing.T) { @@ -173,8 +173,8 @@ func TestMiddlewareAbort(t *testing.T) { w := performRequest(router, "GET", "/") // TEST - assert.Equal(t, w.Code, 401) - assert.Equal(t, signature, "ACD") + assert.Equal(t, 401, w.Code) + assert.Equal(t, "ACD", signature) } func TestMiddlewareAbortHandlersChainAndNext(t *testing.T) { @@ -195,8 +195,8 @@ func TestMiddlewareAbortHandlersChainAndNext(t *testing.T) { w := performRequest(router, "GET", "/") // TEST - assert.Equal(t, w.Code, 410) - assert.Equal(t, signature, "ACB") + assert.Equal(t, 410, w.Code) + assert.Equal(t, "ACB", signature) } // TestFailHandlersChain - ensure that Fail interrupt used middleware in fifo order as @@ -218,8 +218,8 @@ func TestMiddlewareFailHandlersChain(t *testing.T) { w := performRequest(router, "GET", "/") // TEST - assert.Equal(t, w.Code, 500) - assert.Equal(t, signature, "A") + assert.Equal(t, 500, w.Code) + assert.Equal(t, "A", signature) } func TestMiddlewareWrite(t *testing.T) { diff --git a/response_writer_test.go b/response_writer_test.go index 7306d192..cec27338 100644 --- a/response_writer_test.go +++ b/response_writer_test.go @@ -34,11 +34,11 @@ func TestResponseWriterReset(t *testing.T) { var w ResponseWriter = writer writer.reset(testWritter) - assert.Equal(t, writer.size, -1) - assert.Equal(t, writer.status, 200) - assert.Equal(t, writer.ResponseWriter, testWritter) - assert.Equal(t, w.Size(), -1) - assert.Equal(t, w.Status(), 200) + assert.Equal(t, -1, writer.size) + assert.Equal(t, 200, writer.status) + assert.Equal(t, testWritter, writer.ResponseWriter) + assert.Equal(t, -1, w.Size()) + assert.Equal(t, 200, w.Status()) assert.False(t, w.Written()) } @@ -50,11 +50,11 @@ func TestResponseWriterWriteHeader(t *testing.T) { w.WriteHeader(300) assert.False(t, w.Written()) - assert.Equal(t, w.Status(), 300) + assert.Equal(t, 300, w.Status()) assert.NotEqual(t, testWritter.Code, 300) w.WriteHeader(-1) - assert.Equal(t, w.Status(), 300) + assert.Equal(t, 300, w.Status()) } func TestResponseWriterWriteHeadersNow(t *testing.T) { @@ -67,12 +67,12 @@ func TestResponseWriterWriteHeadersNow(t *testing.T) { w.WriteHeaderNow() assert.True(t, w.Written()) - assert.Equal(t, w.Size(), 0) - assert.Equal(t, testWritter.Code, 300) + assert.Equal(t, 0, w.Size()) + assert.Equal(t, 300, testWritter.Code) writer.size = 10 w.WriteHeaderNow() - assert.Equal(t, w.Size(), 10) + assert.Equal(t, 10, w.Size()) } func TestResponseWriterWrite(t *testing.T) { @@ -82,17 +82,17 @@ func TestResponseWriterWrite(t *testing.T) { w := ResponseWriter(writer) n, err := w.Write([]byte("hola")) - assert.Equal(t, n, 4) - assert.Equal(t, w.Size(), 4) - assert.Equal(t, w.Status(), 200) - assert.Equal(t, testWritter.Code, 200) - assert.Equal(t, testWritter.Body.String(), "hola") + assert.Equal(t, 4, n) + assert.Equal(t, 4, w.Size()) + assert.Equal(t, 200, w.Status()) + assert.Equal(t, 200, testWritter.Code) + assert.Equal(t, "hola", testWritter.Body.String()) assert.NoError(t, err) n, err = w.Write([]byte(" adios")) - assert.Equal(t, n, 6) - assert.Equal(t, w.Size(), 10) - assert.Equal(t, testWritter.Body.String(), "hola adios") + assert.Equal(t, 6, n) + assert.Equal(t, 10, w.Size()) + assert.Equal(t, "hola adios", testWritter.Body.String()) assert.NoError(t, err) } From 4b5ec517daa247f8f3bd0448f82ca55c0d14fa0a Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Mon, 14 Aug 2017 11:02:31 +0800 Subject: [PATCH 078/113] fix(test): only check Location header (#1064) * fix(test): only check Location header * fix(test): rename field * fix(test): not export field --- routes_test.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/routes_test.go b/routes_test.go index 41693eed..b44b6431 100644 --- a/routes_test.go +++ b/routes_test.go @@ -356,25 +356,25 @@ func TestRouterNotFound(t *testing.T) { router.GET("/", func(c *Context) {}) testRoutes := []struct { - route string - code int - header string + route string + code int + location string }{ - {"/path/", 301, "map[Location:[/path]]"}, // TSR -/ - {"/dir", 301, "map[Location:[/dir/]]"}, // TSR +/ - {"", 301, "map[Location:[/]]"}, // TSR +/ - {"/PATH", 301, "map[Location:[/path]]"}, // Fixed Case - {"/DIR/", 301, "map[Location:[/dir/]]"}, // Fixed Case - {"/PATH/", 301, "map[Location:[/path]]"}, // Fixed Case -/ - {"/DIR", 301, "map[Location:[/dir/]]"}, // Fixed Case +/ - {"/../path", 301, "map[Location:[/path]]"}, // CleanPath - {"/nope", 404, ""}, // NotFound + {"/path/", 301, "/path"}, // TSR -/ + {"/dir", 301, "/dir/"}, // TSR +/ + {"", 301, "/"}, // TSR +/ + {"/PATH", 301, "/path"}, // Fixed Case + {"/DIR/", 301, "/dir/"}, // Fixed Case + {"/PATH/", 301, "/path"}, // Fixed Case -/ + {"/DIR", 301, "/dir/"}, // Fixed Case +/ + {"/../path", 301, "/path"}, // CleanPath + {"/nope", 404, ""}, // NotFound } for _, tr := range testRoutes { w := performRequest(router, "GET", tr.route) assert.Equal(t, w.Code, tr.code) if w.Code != 404 { - assert.Equal(t, fmt.Sprint(w.Header()), tr.header) + assert.Equal(t, fmt.Sprint(w.Header().Get("Location")), tr.location) } } From 25d20a4463a63083b85bb2d8ebb99c7f638920f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Mon, 14 Aug 2017 12:21:05 +0800 Subject: [PATCH 079/113] merge args if it have same type (#1059) --- gin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 8347ce22..e2c0105d 100644 --- a/gin.go +++ b/gin.go @@ -268,7 +268,7 @@ func (engine *Engine) Run(addr ...string) (err error) { // 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. -func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) { +func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) { debugPrint("Listening and serving HTTPS on %s\n", addr) defer func() { debugPrintError(err) }() From 52c2ed34b3256727a0aeac5e57129eb788bac045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Mon, 14 Aug 2017 14:34:29 +0800 Subject: [PATCH 080/113] log format (#1060) --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index 470e4bb6..7edae415 100644 --- a/logger.go +++ b/logger.go @@ -102,7 +102,7 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { path = path + "?" + raw } - fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s", + fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s", end.Format("2006/01/02 - 15:04:05"), statusColor, statusCode, reset, latency, From ecae34c4e186d5190ca38ac225ce03e3894d99b7 Mon Sep 17 00:00:00 2001 From: keke <19yamashita15@gmail.com> Date: Wed, 16 Aug 2017 10:50:43 +0900 Subject: [PATCH 081/113] fix 200 to http.Status (#1067) --- examples/app-engine/hello.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/app-engine/hello.go b/examples/app-engine/hello.go index da7e4ae4..f569dadb 100644 --- a/examples/app-engine/hello.go +++ b/examples/app-engine/hello.go @@ -13,10 +13,10 @@ func init() { // Define your handlers r.GET("/", func(c *gin.Context) { - c.String(200, "Hello World!") + c.String(http.StatusOK, "Hello World!") }) r.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") + c.String(http.StatusOK, "pong") }) // Handle all requests using net/http From a8fa424ae529397d4a0f2a1f9fda8031851a3269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Wed, 16 Aug 2017 11:55:50 +0800 Subject: [PATCH 082/113] update comment (#1057) --- auth.go | 8 ++++---- context.go | 29 ++++++++++++++--------------- errors.go | 6 +++--- fs.go | 4 ++-- gin.go | 4 ++-- logger.go | 10 +++++----- path.go | 4 ++-- recovery.go | 2 +- render/data.go | 2 +- response_writer.go | 6 +++--- routergroup.go | 18 +++++++++--------- tree.go | 2 +- utils.go | 2 +- 13 files changed, 48 insertions(+), 49 deletions(-) diff --git a/auth.go b/auth.go index c214e213..e7c46bf6 100644 --- a/auth.go +++ b/auth.go @@ -10,10 +10,10 @@ import ( "strconv" ) -// AuthUserKey is the cookie name for user credential in basic auth +// AuthUserKey is the cookie name for user credential in basic auth. const AuthUserKey = "user" -// Accounts defines a key/value for user/pass list of authorized logins +// Accounts defines a key/value for user/pass list of authorized logins. type Accounts map[string]string type authPair struct { @@ -56,7 +56,7 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc { } // The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using - // c.MustGet(gin.AuthUserKey) + // c.MustGet(gin.AuthUserKey). c.Set(AuthUserKey, user) } } @@ -90,6 +90,6 @@ func secureCompare(given, actual string) bool { if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 { return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1 } - // Securely compare actual to itself to keep constant time, but always return false + // Securely compare actual to itself to keep constant time, but always return false. return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false } diff --git a/context.go b/context.go index 895ba7a1..497cbfd6 100644 --- a/context.go +++ b/context.go @@ -22,7 +22,7 @@ import ( "github.com/gin-gonic/gin/render" ) -// Content-Type MIME of the most common data formats +// Content-Type MIME of the most common data formats. const ( MIMEJSON = binding.MIMEJSON MIMEHTML = binding.MIMEHTML @@ -51,13 +51,13 @@ type Context struct { engine *Engine - // Keys is a key/value pair exclusively for the context of each request + // Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} - // Errors is a list of errors attached to all the handlers/middlewares who used this context + // Errors is a list of errors attached to all the handlers/middlewares who used this context. Errors errorMsgs - // Accepted defines a list of manually accepted formats for content negotiation + // Accepted defines a list of manually accepted formats for content negotiation. Accepted []string } @@ -87,7 +87,7 @@ func (c *Context) Copy() *Context { } // HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", -// this function will return "main.handleGetUsers" +// this function will return "main.handleGetUsers". func (c *Context) HandlerName() string { return nameOfFunction(c.handlers.Last()) } @@ -462,18 +462,18 @@ func (c *Context) Bind(obj interface{}) error { return c.MustBindWith(obj, b) } -// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON) +// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON). func (c *Context) BindJSON(obj interface{}) error { return c.MustBindWith(obj, binding.JSON) } -// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query) +// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query). func (c *Context) BindQuery(obj interface{}) error { return c.MustBindWith(obj, binding.Query) } -// MustBindWith binds the passed struct pointer using the specified binding -// engine. It will abort the request with HTTP 400 if any error ocurrs. +// MustBindWith binds the passed struct pointer using the specified binding engine. +// It will abort the request with HTTP 400 if any error ocurrs. // See the binding package. func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) { if err = c.ShouldBindWith(obj, b); err != nil { @@ -483,8 +483,7 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) { return } -// ShouldBindWith binds the passed struct pointer using the specified binding -// engine. +// 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 { return b.Bind(c.Request, obj) @@ -548,7 +547,7 @@ func (c *Context) requestHeader(key string) string { /******** RESPONSE RENDERING ********/ /************************************/ -// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function +// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function. func bodyAllowedForStatus(status int) bool { switch { case status >= 100 && status <= 199: @@ -566,7 +565,7 @@ func (c *Context) Status(code int) { c.writermem.WriteHeader(code) } -// Header is a intelligent shortcut for c.Writer.Header().Set(key, value) +// Header is a intelligent shortcut for c.Writer.Header().Set(key, value). // It writes a header in the response. // If value == "", this method removes the header `c.Writer.Header().Del(key)` func (c *Context) Header(key, value string) { @@ -577,12 +576,12 @@ func (c *Context) Header(key, value string) { } } -// GetHeader returns value from request headers +// GetHeader returns value from request headers. func (c *Context) GetHeader(key string) string { return c.requestHeader(key) } -// GetRawData return stream data +// GetRawData return stream data. func (c *Context) GetRawData() ([]byte, error) { return ioutil.ReadAll(c.Request.Body) } diff --git a/errors.go b/errors.go index 19761106..6f3c9868 100644 --- a/errors.go +++ b/errors.go @@ -65,7 +65,7 @@ func (msg *Error) JSON() interface{} { return json } -// MarshalJSON implements the json.Marshaller interface +// MarshalJSON implements the json.Marshaller interface. func (msg *Error) MarshalJSON() ([]byte, error) { return json.Marshal(msg.JSON()) } @@ -80,7 +80,7 @@ func (msg *Error) IsType(flags ErrorType) bool { } // ByType returns a readonly copy filtered the byte. -// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic +// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic. func (a errorMsgs) ByType(typ ErrorType) errorMsgs { if len(a) == 0 { return nil @@ -98,7 +98,7 @@ func (a errorMsgs) ByType(typ ErrorType) errorMsgs { } // Last returns the last error in the slice. It returns nil if the array is empty. -// Shortcut for errors[len(errors)-1] +// Shortcut for errors[len(errors)-1]. func (a errorMsgs) Last() *Error { if length := len(a); length > 0 { return a[length-1] diff --git a/fs.go b/fs.go index 8570a9a9..7a6738a6 100644 --- a/fs.go +++ b/fs.go @@ -29,7 +29,7 @@ func Dir(root string, listDirectory bool) http.FileSystem { return &onlyfilesFS{fs} } -// Open conforms to http.Filesystem +// Open conforms to http.Filesystem. func (fs onlyfilesFS) Open(name string) (http.File, error) { f, err := fs.fs.Open(name) if err != nil { @@ -38,7 +38,7 @@ func (fs onlyfilesFS) Open(name string) (http.File, error) { return neuteredReaddirFile{f}, nil } -// Readdir overrides the http.File default implementation +// Readdir overrides the http.File default implementation. func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) { // this disables directory listing return nil, nil diff --git a/gin.go b/gin.go index e2c0105d..23853a97 100644 --- a/gin.go +++ b/gin.go @@ -14,7 +14,7 @@ import ( "github.com/gin-gonic/gin/render" ) -// Version is Framework's version +// Version is Framework's version. const Version = "v1.2" var default404Body = []byte("404 page not found") @@ -191,7 +191,7 @@ func (engine *Engine) NoRoute(handlers ...HandlerFunc) { engine.rebuild404Handlers() } -// NoMethod sets the handlers called when... TODO +// NoMethod sets the handlers called when... TODO. func (engine *Engine) NoMethod(handlers ...HandlerFunc) { engine.noMethod = handlers engine.rebuild405Handlers() diff --git a/logger.go b/logger.go index 7edae415..a6f7f140 100644 --- a/logger.go +++ b/logger.go @@ -25,17 +25,17 @@ var ( disableColor = false ) -// DisableConsoleColor disables color output in the console +// DisableConsoleColor disables color output in the console. func DisableConsoleColor() { disableColor = true } -// 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() @@ -46,8 +46,8 @@ func ErrorLoggerT(typ ErrorType) HandlerFunc { } } -// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter -// By default gin.DefaultWriter = os.Stdout +// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter. +// By default gin.DefaultWriter = os.Stdout. func Logger() HandlerFunc { return LoggerWithWriter(DefaultWriter) } diff --git a/path.go b/path.go index e3424b13..ed63ad1a 100644 --- a/path.go +++ b/path.go @@ -17,7 +17,7 @@ package gin // 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 +// If the result of this process is an empty string, "/" is returned. func cleanPath(p string) string { // Turn empty string into "/" if p == "" { @@ -109,7 +109,7 @@ func cleanPath(p string) string { return string(buf[:w]) } -// internal helper to lazily create a buffer if necessary +// internal helper to lazily create a buffer if necessary. func bufApp(buf *[]byte, s string, w int, c byte) { if *buf == nil { if s[w] == c { diff --git a/recovery.go b/recovery.go index c502f355..7aff3d87 100644 --- a/recovery.go +++ b/recovery.go @@ -46,7 +46,7 @@ func RecoveryWithWriter(out io.Writer) HandlerFunc { } } -// stack returns a nicely formated stack frame, skipping skip frames +// stack returns a nicely formated stack frame, skipping skip frames. func stack(skip int) []byte { buf := new(bytes.Buffer) // the returned data // As we loop, we open files and read them. These variables record the currently diff --git a/render/data.go b/render/data.go index c296042c..33194913 100644 --- a/render/data.go +++ b/render/data.go @@ -11,7 +11,7 @@ type Data struct { Data []byte } -// Render (Data) writes data with custom ContentType +// Render (Data) writes data with custom ContentType. func (r Data) Render(w http.ResponseWriter) (err error) { r.WriteContentType(w) _, err = w.Write(r.Data) diff --git a/response_writer.go b/response_writer.go index 216165b9..232f00aa 100644 --- a/response_writer.go +++ b/response_writer.go @@ -95,7 +95,7 @@ func (w *responseWriter) Written() bool { return w.size != noWritten } -// Hijack implements the http.Hijacker interface +// Hijack implements the http.Hijacker interface. func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { if w.size < 0 { w.size = 0 @@ -103,12 +103,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.CloseNotify interface. func (w *responseWriter) CloseNotify() <-chan bool { return w.ResponseWriter.(http.CloseNotifier).CloseNotify() } -// Flush implements the http.Flush interface +// Flush implements the http.Flush interface. func (w *responseWriter) Flush() { w.ResponseWriter.(http.Flusher).Flush() } diff --git a/routergroup.go b/routergroup.go index 89ec89aa..5e681c1c 100644 --- a/routergroup.go +++ b/routergroup.go @@ -35,7 +35,7 @@ type IRoutes interface { } // RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix -// and an array of handlers (middleware) +// and an array of handlers (middleware). type RouterGroup struct { Handlers HandlersChain basePath string @@ -89,43 +89,43 @@ 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, handle). func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle("POST", relativePath, handlers) } -// GET is a shortcut for router.Handle("GET", path, handle) +// GET is a shortcut for router.Handle("GET", path, handle). func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle("GET", relativePath, handlers) } -// DELETE is a shortcut for router.Handle("DELETE", path, handle) +// DELETE is a shortcut for router.Handle("DELETE", path, handle). func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle("DELETE", relativePath, handlers) } -// PATCH is a shortcut for router.Handle("PATCH", path, handle) +// PATCH is a shortcut for router.Handle("PATCH", path, handle). func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle("PATCH", relativePath, handlers) } -// PUT is a shortcut for router.Handle("PUT", path, handle) +// PUT is a shortcut for router.Handle("PUT", path, handle). func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle("PUT", relativePath, handlers) } -// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle) +// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle). func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle("OPTIONS", relativePath, handlers) } -// HEAD is a shortcut for router.Handle("HEAD", path, handle) +// HEAD is a shortcut for router.Handle("HEAD", path, handle). func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle("HEAD", relativePath, handlers) } // Any registers a route that matches all the HTTP methods. -// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE +// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE. func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes { group.handle("GET", relativePath, handlers) group.handle("POST", relativePath, handlers) diff --git a/tree.go b/tree.go index 750ffae8..f67edd5d 100644 --- a/tree.go +++ b/tree.go @@ -96,7 +96,7 @@ type node struct { priority uint32 } -// increments priority of the given child and reorders if necessary +// increments priority of the given child and reorders if necessary. func (n *node) incrementChildPrio(pos int) int { n.children[pos].priority++ prio := n.children[pos].priority diff --git a/utils.go b/utils.go index 968570c7..ab06a759 100644 --- a/utils.go +++ b/utils.go @@ -47,7 +47,7 @@ func WrapH(h http.Handler) HandlerFunc { type H map[string]interface{} -// MarshalXML allows type H to be used with xml.Marshal +// MarshalXML allows type H to be used with xml.Marshal. func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error { start.Name = xml.Name{ Space: "", From 3856206bd07d1a5627eb951e345fff25f9b6209c Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Thu, 17 Aug 2017 12:18:50 +0800 Subject: [PATCH 083/113] doc(readme): add additional middleware doc. (#1056) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b14c2a2a..dc3c8667 100644 --- a/README.md +++ b/README.md @@ -369,6 +369,7 @@ r := gin.New() instead of ```go +// Default With the Logger and Recovery middleware already attached r := gin.Default() ``` @@ -380,7 +381,10 @@ func main() { r := gin.New() // Global middleware + // Logger middleware will write the logs to gin.DefaultWriter even 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. From f4c9ac17a49e90c14ffe08b9e50aa4ee6abf6490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 22 Aug 2017 10:27:28 +0800 Subject: [PATCH 084/113] not display color when set disableColor (#1072) --- logger.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/logger.go b/logger.go index a6f7f140..c679c787 100644 --- a/logger.go +++ b/logger.go @@ -91,10 +91,11 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { clientIP := c.ClientIP() method := c.Request.Method statusCode := c.Writer.Status() - var statusColor, methodColor string + var statusColor, methodColor, resetColor string if isTerm { statusColor = colorForStatus(statusCode) methodColor = colorForMethod(method) + resetColor = reset } comment := c.Errors.ByType(ErrorTypePrivate).String() @@ -104,10 +105,10 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s", end.Format("2006/01/02 - 15:04:05"), - statusColor, statusCode, reset, + statusColor, statusCode, resetColor, latency, clientIP, - methodColor, method, reset, + methodColor, method, resetColor, path, comment, ) From 8be30bd382890156e7e813beec9f18b2c65522a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 22 Aug 2017 13:55:32 +0800 Subject: [PATCH 085/113] Update readme for showing output log to file (#1073) * Update readme for showing output log to file * update indent --- README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc3c8667..776ae8b8 100644 --- a/README.md +++ b/README.md @@ -381,7 +381,8 @@ func main() { r := gin.New() // Global middleware - // Logger middleware will write the logs to gin.DefaultWriter even you set with GIN_MODE=release. By default gin.DefaultWriter = os.Stdout + // Logger middleware will write the logs to gin.DefaultWriter even 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. @@ -412,6 +413,27 @@ func main() { } ``` +### Output log to file +```go +func main() { + // Disable Console Color, because not need color when output log to file + gin.DisableConsoleColor() + // Create one file to save logs + f, _ := os.Create("gin.log") + // Reset gin.DefaultWriter + gin.DefaultWriter = io.MultiWriter(f) + // If need to output log to file and console at a time, please use the following code: + // gin.DefaultWriter = io.MultiWriter(f, os.Stdout) + + router := gin.Default() + router.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + r.Run(":8080") +} +``` + ### Model binding and validation To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz). From 80152ac82c413e82275e828fdde8892c458715f7 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Thu, 24 Aug 2017 10:50:31 +0800 Subject: [PATCH 086/113] doc(readme): update writing logs section wording. (#1074) * doc(readme): update writing logs section wording. * doc(readme): update writing logs section wording. * doc(readme): fix word formatting --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 776ae8b8..b5644464 100644 --- a/README.md +++ b/README.md @@ -413,16 +413,17 @@ func main() { } ``` -### Output log to file +### How to write log file ```go func main() { - // Disable Console Color, because not need color when output log to file + // Disable Console Color, you don't need console color when writing the logs to file. gin.DisableConsoleColor() - // Create one file to save logs + + // Logging to a file. f, _ := os.Create("gin.log") - // Reset gin.DefaultWriter gin.DefaultWriter = io.MultiWriter(f) - // If need to output log to file and console at a time, please use the following code: + + // 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() From c25254f563fc3685e22901edf2fd289902a76cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Fri, 25 Aug 2017 09:00:49 +0800 Subject: [PATCH 087/113] reduce go cyclo (#1076) * reduce go cyclo * use := var --- gin.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/gin.go b/gin.go index 23853a97..ee39c9aa 100644 --- a/gin.go +++ b/gin.go @@ -316,14 +316,11 @@ func (engine *Engine) HandleContext(c *Context) { func (engine *Engine) handleHTTPRequest(context *Context) { httpMethod := context.Request.Method - var path string - var unescape bool + path := context.Request.URL.Path + unescape := false if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 { path = context.Request.URL.RawPath unescape = engine.UnescapePathValues - } else { - path = context.Request.URL.Path - unescape = false } // Find root of the tree for the given HTTP method From bc538849ebc41f2600338d9f65fad2c3008c95b2 Mon Sep 17 00:00:00 2001 From: stackerzzq Date: Fri, 25 Aug 2017 09:06:13 +0800 Subject: [PATCH 088/113] Update readme for adding tag example of binding time field (#1080) --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b5644464..879d77b4 100644 --- a/README.md +++ b/README.md @@ -532,10 +532,12 @@ package main import "log" import "github.com/gin-gonic/gin" +import "time" type Person struct { - Name string `form:"name"` - Address string `form:"address"` + Name string `form:"name"` + Address string `form:"address"` + Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` } func main() { @@ -552,12 +554,18 @@ func startPage(c *gin.Context) { if c.Bind(&person) == nil { log.Println(person.Name) log.Println(person.Address) + log.Println(person.Birthday) } c.String(200, "Success") } ``` +Test it with: +```sh +$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15" +``` + ### Bind HTML checkboxes See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092) From 18b7c0892df51ba799473e57fd3d65f91c1c3aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Fri, 25 Aug 2017 09:13:53 +0800 Subject: [PATCH 089/113] not use dot when import package (#1077) --- ginS/gins.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ginS/gins.go b/ginS/gins.go index d40d1c3a..d238fcae 100644 --- a/ginS/gins.go +++ b/ginS/gins.go @@ -9,15 +9,15 @@ import ( "net/http" "sync" - . "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) var once sync.Once -var internalEngine *Engine +var internalEngine *gin.Engine -func engine() *Engine { +func engine() *gin.Engine { once.Do(func() { - internalEngine = Default() + internalEngine = gin.Default() }) return internalEngine } From b9686e91fa271d2fe5da3108a2ea4087c0b0ad68 Mon Sep 17 00:00:00 2001 From: Dan Markham Date: Fri, 25 Aug 2017 21:53:27 -0700 Subject: [PATCH 090/113] Use standard library for retrieving header (#1081) --- context.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/context.go b/context.go index 497cbfd6..6f2b0053 100644 --- a/context.go +++ b/context.go @@ -537,10 +537,7 @@ func (c *Context) IsWebsocket() bool { } func (c *Context) requestHeader(key string) string { - if values, _ := c.Request.Header[key]; len(values) > 0 { - return values[0] - } - return "" + return c.Request.Header.Get(key) } /************************************/ From fa391a4864cb2e249b4e82fd1d71261a1496f0f4 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 26 Aug 2017 03:48:56 -0500 Subject: [PATCH 091/113] chore(ci): add go 1.9 version (#1082) Signed-off-by: Bo-Yi Wu --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 821ce8df..ec12cad6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ go: - 1.6.x - 1.7.x - 1.8.x + - 1.9.x - master git: From 211c48f0408804fdbc3b698cf53243da9699c376 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 26 Aug 2017 05:02:47 -0500 Subject: [PATCH 092/113] refactor: using requestHeader internal func (#1083) * refactor: using requestHeader internal func. * update Signed-off-by: Bo-Yi Wu --- auth.go | 2 +- context.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/auth.go b/auth.go index e7c46bf6..85d96fd3 100644 --- a/auth.go +++ b/auth.go @@ -47,7 +47,7 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc { pairs := processAccounts(accounts) return func(c *Context) { // Search user in the slice of allowed credentials - user, found := pairs.searchCredential(c.Request.Header.Get("Authorization")) + user, found := pairs.searchCredential(c.requestHeader("Authorization")) if !found { // Credentials doesn't match, we return 401 and abort handlers chain. c.Header("WWW-Authenticate", realm) diff --git a/context.go b/context.go index 6f2b0053..2be7010d 100644 --- a/context.go +++ b/context.go @@ -509,7 +509,7 @@ func (c *Context) ClientIP() string { } if c.engine.AppEngine { - if addr := c.Request.Header.Get("X-Appengine-Remote-Addr"); addr != "" { + if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { return addr } } From 030b1aaf72ce020aa38b6c4bbdd6a4baf746051d Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 26 Aug 2017 12:37:19 -0500 Subject: [PATCH 093/113] chore(ci): replace travis ci go 1.9.x for 1.9 (#1085) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ec12cad6..72daa3f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ go: - 1.6.x - 1.7.x - 1.8.x - - 1.9.x + - 1.9 - master git: From 26c3f42095b8a1378617eb4756fbf8282cb1ae89 Mon Sep 17 00:00:00 2001 From: Suhas Karanth Date: Sun, 27 Aug 2017 13:07:39 +0530 Subject: [PATCH 094/113] feat(binding): add support for custom validator / validation tags (#1068) * feat(binding): Add support for custom validation tags * docs: Add example for custom validation tag * test(binding): Add test for registering custom validation --- README.md | 85 ++++++++++++++++++++++++---- binding/binding.go | 11 +++- binding/default_validator.go | 5 ++ binding/validate_test.go | 42 ++++++++++++++ examples/custom-validation/server.go | 45 +++++++++++++++ 5 files changed, 175 insertions(+), 13 deletions(-) create mode 100644 examples/custom-validation/server.go diff --git a/README.md b/README.md index 879d77b4..07357be4 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ $ go run example.go ## Benchmarks -Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter) +Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter) [See all benchmarks](/BENCHMARKS.md) @@ -74,10 +74,10 @@ BenchmarkTigerTonic_GithubAll | 1000 | 1439483 | 239104 BenchmarkTraffic_GithubAll | 100 | 11383067 | 2659329 | 21848 BenchmarkVulcan_GithubAll | 5000 | 394253 | 19894 | 609 -(1): Total Repetitions achieved in constant time, higher means more confident result -(2): Single Repetition Duration (ns/op), lower is better -(3): Heap Memory (B/op), lower is better -(4): Average Allocations per Repetition (allocs/op), lower is better +(1): Total Repetitions achieved in constant time, higher means more confident result +(2): Single Repetition Duration (ns/op), lower is better +(3): Heap Memory (B/op), lower is better +(4): Average Allocations per Repetition (allocs/op), lower is better ## Gin v1. stable @@ -281,10 +281,10 @@ func main() { // single file file, _ := c.FormFile("file") log.Println(file.Filename) - + // Upload the file to specific dst. - // c.SaveUploadedFile(file, dst) - + // c.SaveUploadedFile(file, dst) + c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) }) router.Run(":8080") @@ -313,9 +313,9 @@ func main() { for _, file := range files { log.Println(file.Filename) - + // Upload the file to specific dst. - // c.SaveUploadedFile(file, dst) + // c.SaveUploadedFile(file, dst) } c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) }) @@ -487,6 +487,67 @@ func main() { } ``` +### Custom Validators + +It is also possible to register custom validators. See the [example code](examples/custom-validation/server.go). + +[embedmd]:# (examples/custom-validation/server.go go) +```go +package main + +import ( + "net/http" + "reflect" + "time" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + validator "gopkg.in/go-playground/validator.v8" +) + +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"` +} + +func bookableDate( + v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, + field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, +) bool { + if date, ok := field.Interface().(time.Time); ok { + today := time.Now() + if today.Year() > date.Year() || today.YearDay() > date.YearDay() { + return false + } + } + return true +} + +func main() { + route := gin.Default() + binding.Validator.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=2017-08-16&check_out=2017-08-17" +{"message":"Booking dates are valid!"} + +$ curl "localhost:8085/bookable?check_in=2017-08-15&check_out=2017-08-16" +{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"} +``` + ### Only Bind Query String `BindQuery` 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). @@ -711,7 +772,7 @@ func main() { // Listen and serve on 0.0.0.0:8080 r.Run(":8080") } -``` +``` ### Serving static files @@ -822,7 +883,7 @@ You may use custom delims r := gin.Default() r.Delims("{[{", "}]}") r.LoadHTMLGlob("/path/to/templates")) -``` +``` #### Custom Template Funcs diff --git a/binding/binding.go b/binding/binding.go index 971547c2..a09cc221 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -4,7 +4,11 @@ package binding -import "net/http" +import ( + "net/http" + + validator "gopkg.in/go-playground/validator.v8" +) const ( MIMEJSON = "application/json" @@ -31,6 +35,11 @@ type StructValidator interface { // 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 + + // RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key + // NOTE: if the key already exists, the previous validation function will be replaced. + // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation + RegisterValidation(string, validator.Func) error } var Validator StructValidator = &defaultValidator{} diff --git a/binding/default_validator.go b/binding/default_validator.go index 19885f16..6336bb6e 100644 --- a/binding/default_validator.go +++ b/binding/default_validator.go @@ -28,6 +28,11 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error { return nil } +func (v *defaultValidator) RegisterValidation(key string, fn validator.Func) error { + v.lazyinit() + return v.validate.RegisterValidation(key, fn) +} + func (v *defaultValidator) lazyinit() { v.once.Do(func() { config := &validator.Config{TagName: "binding"} diff --git a/binding/validate_test.go b/binding/validate_test.go index cbcb389d..523e1298 100644 --- a/binding/validate_test.go +++ b/binding/validate_test.go @@ -6,9 +6,12 @@ package binding import ( "bytes" + "reflect" "testing" "time" + validator "gopkg.in/go-playground/validator.v8" + "github.com/stretchr/testify/assert" ) @@ -190,3 +193,42 @@ func TestValidatePrimitives(t *testing.T) { assert.NoError(t, validate(&str)) assert.Equal(t, str, "value") } + +// 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. +type structCustomValidation struct { + Integer int `binding:"notone"` +} + +// notOne is a custom validator meant to be used with `validator.v8` library. +// The method signature for `v9` is significantly different and this function +// would need to be changed for tests to pass after upgrade. +// See https://github.com/gin-gonic/gin/pull/1015. +func notOne( + v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, + field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, +) bool { + if val, ok := field.Interface().(int); ok { + return val != 1 + } + return false +} + +func TestRegisterValidation(t *testing.T) { + // This validates that the function `notOne` matches + // the expected function signature by `defaultValidator` + // and by extension the validator library. + err := Validator.RegisterValidation("notone", notOne) + // Check that we can register custom validation without error + assert.Nil(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) + // Check that the error matches expactation + assert.Error(t, errs, "", "", "notone") +} diff --git a/examples/custom-validation/server.go b/examples/custom-validation/server.go new file mode 100644 index 00000000..0b67ce10 --- /dev/null +++ b/examples/custom-validation/server.go @@ -0,0 +1,45 @@ +package main + +import ( + "net/http" + "reflect" + "time" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + validator "gopkg.in/go-playground/validator.v8" +) + +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"` +} + +func bookableDate( + v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, + field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, +) bool { + if date, ok := field.Interface().(time.Time); ok { + today := time.Now() + if today.Year() > date.Year() || today.YearDay() > date.YearDay() { + return false + } + } + return true +} + +func main() { + route := gin.Default() + binding.Validator.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()}) + } +} From f1edd2c2d1c0ff06ab0a3e8f3c6a3d58ee4a0a68 Mon Sep 17 00:00:00 2001 From: vz Date: Sun, 27 Aug 2017 17:11:38 +0800 Subject: [PATCH 095/113] fix(ginS): fix undefined ref (#1087) --- ginS/gins.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ginS/gins.go b/ginS/gins.go index d238fcae..ee00b381 100644 --- a/ginS/gins.go +++ b/ginS/gins.go @@ -35,65 +35,65 @@ func SetHTMLTemplate(templ *template.Template) { } // NoRoute adds handlers for NoRoute. It return a 404 code by default. -func NoRoute(handlers ...HandlerFunc) { +func NoRoute(handlers ...gin.HandlerFunc) { engine().NoRoute(handlers...) } // NoMethod sets the handlers called when... TODO -func NoMethod(handlers ...HandlerFunc) { +func NoMethod(handlers ...gin.HandlerFunc) { engine().NoMethod(handlers...) } // Group creates a new router group. You should add all the routes that have common middlwares or the same path prefix. // For example, all the routes that use a common middlware for authorization could be grouped. -func Group(relativePath string, handlers ...HandlerFunc) *RouterGroup { +func Group(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup { return engine().Group(relativePath, handlers...) } -func Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes { +func Handle(httpMethod, relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { return engine().Handle(httpMethod, relativePath, handlers...) } // POST is a shortcut for router.Handle("POST", path, handle) -func POST(relativePath string, handlers ...HandlerFunc) IRoutes { +func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { return engine().POST(relativePath, handlers...) } // GET is a shortcut for router.Handle("GET", path, handle) -func GET(relativePath string, handlers ...HandlerFunc) IRoutes { +func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { return engine().GET(relativePath, handlers...) } // DELETE is a shortcut for router.Handle("DELETE", path, handle) -func DELETE(relativePath string, handlers ...HandlerFunc) IRoutes { +func DELETE(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { return engine().DELETE(relativePath, handlers...) } // PATCH is a shortcut for router.Handle("PATCH", path, handle) -func PATCH(relativePath string, handlers ...HandlerFunc) IRoutes { +func PATCH(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { return engine().PATCH(relativePath, handlers...) } // PUT is a shortcut for router.Handle("PUT", path, handle) -func PUT(relativePath string, handlers ...HandlerFunc) IRoutes { +func PUT(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { return engine().PUT(relativePath, handlers...) } // OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle) -func OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes { +func OPTIONS(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { return engine().OPTIONS(relativePath, handlers...) } // HEAD is a shortcut for router.Handle("HEAD", path, handle) -func HEAD(relativePath string, handlers ...HandlerFunc) IRoutes { +func HEAD(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { return engine().HEAD(relativePath, handlers...) } -func Any(relativePath string, handlers ...HandlerFunc) IRoutes { +func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { return engine().Any(relativePath, handlers...) } -func StaticFile(relativePath, filepath string) IRoutes { +func StaticFile(relativePath, filepath string) gin.IRoutes { return engine().StaticFile(relativePath, filepath) } @@ -103,18 +103,18 @@ func StaticFile(relativePath, filepath string) IRoutes { // To use the operating system's file system implementation, // use : // router.Static("/static", "/var/www") -func Static(relativePath, root string) IRoutes { +func Static(relativePath, root string) gin.IRoutes { return engine().Static(relativePath, root) } -func StaticFS(relativePath string, fs http.FileSystem) IRoutes { +func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes { return engine().StaticFS(relativePath, fs) } // Use attachs a global middleware to the router. ie. the middlewares attached though Use() will be // included in the handlers chain for every single request. Even 404, 405, static files... // For example, this is the right place for a logger or error management middleware. -func Use(middlewares ...HandlerFunc) IRoutes { +func Use(middlewares ...gin.HandlerFunc) gin.IRoutes { return engine().Use(middlewares...) } From 8902826696c1dad024e1c8ba4f903aa61a25c839 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Tue, 29 Aug 2017 03:38:53 +0800 Subject: [PATCH 096/113] doc(context): add cookie doc (#1088) --- context.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/context.go b/context.go index 2be7010d..d7b6c61d 100644 --- a/context.go +++ b/context.go @@ -583,6 +583,9 @@ func (c *Context) GetRawData() ([]byte, error) { return ioutil.ReadAll(c.Request.Body) } +// SetCookie adds a Set-Cookie header to the ResponseWriter's headers. +// The provided cookie must have a valid Name. Invalid cookies may be +// silently dropped. func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) { if path == "" { path = "/" @@ -598,6 +601,10 @@ func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, }) } +// Cookie returns the named cookie provided in the request or +// ErrNoCookie if not found. And return the named cookie is unescaped. +// If multiple cookies match the given name, only one cookie will +// be returned. func (c *Context) Cookie(name string) (string, error) { cookie, err := c.Request.Cookie(name) if err != nil { From c16c2b7ec33175b23667be512a74dbb7f3943aa5 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Wed, 30 Aug 2017 12:18:57 +0800 Subject: [PATCH 097/113] chore(vendor): update jsoniter rev, #1086 (#1090) --- vendor/vendor.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index e8690a2c..bcefee10 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -34,10 +34,10 @@ "revisionTime": "2017-06-01T23:02:30Z" }, { - "checksumSHA1": "0e59uuETpidkmpaRwipQ8auqwhM=", + "checksumSHA1": "Ajh8TemnItg4nn+jKmVcsMRALBc=", "path": "github.com/json-iterator/go", - "revision": "6b6938829d6156d7b9825f83eec757f0f571c981", - "revisionTime": "2017-07-18T14:19:52Z" + "revision": "36b14963da70d11297d313183d7e6388c8510e1e", + "revisionTime": "2017-08-29T15:58:51Z" }, { "checksumSHA1": "9if9IBLsxkarJ804NPWAzgskIAk=", From ab50cf97900b63748a62a81c06c9189ef536d121 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Fri, 1 Sep 2017 15:02:25 +0100 Subject: [PATCH 098/113] correct spelling mistake (#1092) --- recovery.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recovery.go b/recovery.go index 7aff3d87..dfea8c23 100644 --- a/recovery.go +++ b/recovery.go @@ -46,7 +46,7 @@ func RecoveryWithWriter(out io.Writer) HandlerFunc { } } -// stack returns a nicely formated stack frame, skipping skip frames. +// stack returns a nicely formatted stack frame, skipping skip frames. func stack(skip int) []byte { buf := new(bytes.Buffer) // the returned data // As we loop, we open files and read them. These variables record the currently From cdf26f994bb551da109f4e4b8f206d8db4fdb354 Mon Sep 17 00:00:00 2001 From: George Kirilenko Date: Mon, 4 Sep 2017 04:15:50 +0300 Subject: [PATCH 099/113] 32 << 10 != 32 Mb (#1094) --- binding/form.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/binding/form.go b/binding/form.go index 557333e6..0be59660 100644 --- a/binding/form.go +++ b/binding/form.go @@ -6,6 +6,8 @@ package binding import "net/http" +const defaultMemory = 32 * 1024 * 1024 + type formBinding struct{} type formPostBinding struct{} type formMultipartBinding struct{} @@ -18,7 +20,7 @@ func (formBinding) Bind(req *http.Request, obj interface{}) error { if err := req.ParseForm(); err != nil { return err } - req.ParseMultipartForm(32 << 10) // 32 MB + req.ParseMultipartForm(defaultMemory) if err := mapForm(obj, req.Form); err != nil { return err } @@ -44,7 +46,7 @@ func (formMultipartBinding) Name() string { } func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error { - if err := req.ParseMultipartForm(32 << 10); err != nil { + if err := req.ParseMultipartForm(defaultMemory); err != nil { return err } if err := mapForm(obj, req.MultipartForm.Value); err != nil { From 848fa41ca016fa3a3d385af710c4219c1cb477a4 Mon Sep 17 00:00:00 2001 From: Eason Lin Date: Tue, 5 Sep 2017 09:17:53 +0800 Subject: [PATCH 100/113] doc(recovery): add RecoveryWithWriter doc (#1097) --- recovery.go | 1 + 1 file changed, 1 insertion(+) diff --git a/recovery.go b/recovery.go index dfea8c23..89b39fec 100644 --- a/recovery.go +++ b/recovery.go @@ -26,6 +26,7 @@ func Recovery() HandlerFunc { return RecoveryWithWriter(DefaultErrorWriter) } +// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one. func RecoveryWithWriter(out io.Writer) HandlerFunc { var logger *log.Logger if out != nil { From c9b344118f6426e9211c2e4530ac983a541f437a Mon Sep 17 00:00:00 2001 From: "Daniel M. Lambea" Date: Thu, 7 Sep 2017 04:45:16 +0100 Subject: [PATCH 101/113] Moved const 'defaultMemory' to attrib. Engine.MaxMultipartMemory instead. (#1100) --- README.md | 4 ++++ context.go | 7 +++---- examples/upload-file/multiple/main.go | 2 ++ examples/upload-file/single/main.go | 2 ++ gin.go | 10 +++++++++- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 07357be4..d16a8682 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,8 @@ 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") @@ -306,6 +308,8 @@ See the detail [example code](examples/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() diff --git a/context.go b/context.go index d7b6c61d..74995e68 100644 --- a/context.go +++ b/context.go @@ -34,8 +34,7 @@ const ( ) const ( - defaultMemory = 32 << 20 // 32 MB - abortIndex int8 = math.MaxInt8 / 2 + abortIndex int8 = math.MaxInt8 / 2 ) // Context is the most important part of gin. It allows us to pass variables between middleware, @@ -407,7 +406,7 @@ func (c *Context) PostFormArray(key string) []string { func (c *Context) GetPostFormArray(key string) ([]string, bool) { req := c.Request req.ParseForm() - req.ParseMultipartForm(defaultMemory) + req.ParseMultipartForm(c.engine.MaxMultipartMemory) if values := req.PostForm[key]; len(values) > 0 { return values, true } @@ -427,7 +426,7 @@ func (c *Context) FormFile(name string) (*multipart.FileHeader, error) { // MultipartForm is the parsed multipart form, including file uploads. func (c *Context) MultipartForm() (*multipart.Form, error) { - err := c.Request.ParseMultipartForm(defaultMemory) + err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory) return c.Request.MultipartForm, err } diff --git a/examples/upload-file/multiple/main.go b/examples/upload-file/multiple/main.go index 4bb4cdcb..a55325ed 100644 --- a/examples/upload-file/multiple/main.go +++ b/examples/upload-file/multiple/main.go @@ -9,6 +9,8 @@ import ( func main() { router := gin.Default() + // Set a lower memory limit for multipart forms (default is 32 MiB) + router.MaxMultipartMemory = 8 << 20 // 8 MiB router.Static("/", "./public") router.POST("/upload", func(c *gin.Context) { name := c.PostForm("name") diff --git a/examples/upload-file/single/main.go b/examples/upload-file/single/main.go index 372a2994..5d438651 100644 --- a/examples/upload-file/single/main.go +++ b/examples/upload-file/single/main.go @@ -9,6 +9,8 @@ import ( func main() { router := gin.Default() + // Set a lower memory limit for multipart forms (default is 32 MiB) + router.MaxMultipartMemory = 8 << 20 // 8 MiB router.Static("/", "./public") router.POST("/upload", func(c *gin.Context) { name := c.PostForm("name") diff --git a/gin.go b/gin.go index ee39c9aa..24c4dd8d 100644 --- a/gin.go +++ b/gin.go @@ -15,7 +15,10 @@ import ( ) // Version is Framework's version. -const Version = "v1.2" +const ( + Version = "v1.2" + defaultMultipartMemory = 32 << 20 // 32 MB +) var default404Body = []byte("404 page not found") var default405Body = []byte("405 method not allowed") @@ -92,6 +95,10 @@ type Engine struct { // If UseRawPath is false (by default), the UnescapePathValues effectively is true, // as url.Path gonna be used, which is already unescaped. UnescapePathValues bool + + // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm + // method call. + MaxMultipartMemory int64 } var _ IRouter = &Engine{} @@ -120,6 +127,7 @@ func New() *Engine { AppEngine: defaultAppEngine, UseRawPath: false, UnescapePathValues: true, + MaxMultipartMemory: defaultMultipartMemory, trees: make(methodTrees, 0, 9), delims: render.Delims{Left: "{{", Right: "}}"}, secureJsonPrefix: "while(1);", From b1ee49de8c80ea72acf019315d3dffc0ce6e06ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Fri, 8 Sep 2017 08:56:56 +0800 Subject: [PATCH 102/113] fix typo (#1103) --- deprecated.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deprecated.go b/deprecated.go index 7b50dc70..ab447429 100644 --- a/deprecated.go +++ b/deprecated.go @@ -15,7 +15,7 @@ import ( func (c *Context) BindWith(obj interface{}, 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, of use + want HTTP 400 to be automatically returned if any error occur, or use ShouldBindWith() if you need to manage the error.`) return c.MustBindWith(obj, b) } From 8c17c680d9e4a6a6d7c2fd48eafe463eac23372e Mon Sep 17 00:00:00 2001 From: "Kristoffer A. Iversen" Date: Mon, 11 Sep 2017 16:17:26 +0200 Subject: [PATCH 103/113] Fixed README.md typo (#1104) * Fixed README.md typo * Fixed example typo * Fixed example typo --- README.md | 2 +- examples/graceful-shutdown/close/server.go | 2 +- examples/graceful-shutdown/graceful-shutdown/server.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d16a8682..0dc165bc 100644 --- a/README.md +++ b/README.md @@ -1224,7 +1224,7 @@ func main() { if err := srv.Shutdown(ctx); err != nil { log.Fatal("Server Shutdown:", err) } - log.Println("Server exist") + log.Println("Server exiting") } ``` diff --git a/examples/graceful-shutdown/close/server.go b/examples/graceful-shutdown/close/server.go index 54778393..9c4e90fa 100644 --- a/examples/graceful-shutdown/close/server.go +++ b/examples/graceful-shutdown/close/server.go @@ -41,5 +41,5 @@ func main() { } } - log.Println("Server exist") + log.Println("Server exiting") } diff --git a/examples/graceful-shutdown/graceful-shutdown/server.go b/examples/graceful-shutdown/graceful-shutdown/server.go index 060de081..6debe7f5 100644 --- a/examples/graceful-shutdown/graceful-shutdown/server.go +++ b/examples/graceful-shutdown/graceful-shutdown/server.go @@ -44,5 +44,5 @@ func main() { if err := srv.Shutdown(ctx); err != nil { log.Fatal("Server Shutdown:", err) } - log.Println("Server exist") + log.Println("Server exiting") } From 5afc5b19730118c9b8324fe9dd995d44ec65c81a Mon Sep 17 00:00:00 2001 From: Davor Kapsa Date: Mon, 11 Sep 2017 16:33:19 +0200 Subject: [PATCH 104/113] travis: add 1.9.x instead 1.9 to go version (#1105) 1.9.x should be working now. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 72daa3f3..ec12cad6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ go: - 1.6.x - 1.7.x - 1.8.x - - 1.9 + - 1.9.x - master git: From a8c53949e5b7d8a00a9e26b1609a7626e7d0b127 Mon Sep 17 00:00:00 2001 From: delphinus Date: Thu, 28 Sep 2017 23:23:18 +0900 Subject: [PATCH 105/113] Support time location on form binding (#1117) --- binding/form_mapping.go | 8 ++++++++ context_test.go | 21 +++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 34f12678..c968dc08 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -163,6 +163,14 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val l = time.UTC } + if locTag := structField.Tag.Get("time_location"); locTag != "" { + loc, err := time.LoadLocation(locTag) + if err != nil { + return err + } + l = loc + } + t, err := time.ParseInLocation(timeFormat, val, l) if err != nil { return err diff --git a/context_test.go b/context_test.go index 15569bf2..4854a8a8 100644 --- a/context_test.go +++ b/context_test.go @@ -45,6 +45,7 @@ func createMultipartRequest() *http.Request { must(mw.WriteField("id", "")) must(mw.WriteField("time_local", "31/12/2016 14:55")) must(mw.WriteField("time_utc", "31/12/2016 14:55")) + must(mw.WriteField("time_location", "31/12/2016 14:55")) req, err := http.NewRequest("POST", "/", body) must(err) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) @@ -444,14 +445,15 @@ func TestContextPostFormMultipart(t *testing.T) { c.Request = createMultipartRequest() var obj struct { - Foo string `form:"foo"` - Bar string `form:"bar"` - BarAsInt int `form:"bar"` - Array []string `form:"array"` - ID string `form:"id"` - TimeLocal time.Time `form:"time_local" time_format:"02/01/2006 15:04"` - TimeUTC time.Time `form:"time_utc" time_format:"02/01/2006 15:04" time_utc:"1"` - BlankTime time.Time `form:"blank_time" time_format:"02/01/2006 15:04"` + Foo string `form:"foo"` + Bar string `form:"bar"` + BarAsInt int `form:"bar"` + Array []string `form:"array"` + ID string `form:"id"` + TimeLocal time.Time `form:"time_local" time_format:"02/01/2006 15:04"` + TimeUTC time.Time `form:"time_utc" time_format:"02/01/2006 15:04" time_utc:"1"` + 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)) assert.Equal(t, obj.Foo, "bar") @@ -463,6 +465,9 @@ func TestContextPostFormMultipart(t *testing.T) { assert.Equal(t, obj.TimeLocal.Location(), time.Local) assert.Equal(t, obj.TimeUTC.Format("02/01/2006 15:04"), "31/12/2016 14:55") assert.Equal(t, obj.TimeUTC.Location(), time.UTC) + loc, _ := time.LoadLocation("Asia/Tokyo") + assert.Equal(t, obj.TimeLocation.Format("02/01/2006 15:04"), "31/12/2016 14:55") + assert.Equal(t, obj.TimeLocation.Location(), loc) assert.True(t, obj.BlankTime.IsZero()) value, ok := c.GetQuery("foo") From f7376f7c7fc73e2fdd30623f413f87a32e73baa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Thu, 28 Sep 2017 09:54:37 -0500 Subject: [PATCH 106/113] combine var and use tmp var (#1108) --- gin.go | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/gin.go b/gin.go index 24c4dd8d..3a51bc14 100644 --- a/gin.go +++ b/gin.go @@ -20,9 +20,11 @@ const ( defaultMultipartMemory = 32 << 20 // 32 MB ) -var default404Body = []byte("404 page not found") -var default405Body = []byte("405 method not allowed") -var defaultAppEngine bool +var ( + default404Body = []byte("404 page not found") + default405Body = []byte("405 method not allowed") + defaultAppEngine bool +) type HandlerFunc func(*Context) type HandlersChain []HandlerFunc @@ -91,6 +93,7 @@ type Engine struct { // If enabled, the url.RawPath will be used to find parameters. UseRawPath bool + // If true, the path value will be unescaped. // If UseRawPath is false (by default), the UnescapePathValues effectively is true, // as url.Path gonna be used, which is already unescaped. @@ -161,13 +164,16 @@ func (engine *Engine) SecureJsonPrefix(prefix string) *Engine { } func (engine *Engine) LoadHTMLGlob(pattern string) { + left := engine.delims.Left + right := engine.delims.Right + if IsDebugging() { - debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern))) + debugPrintLoadTemplate(template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))) engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims} return } - templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)) + templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern)) engine.SetHTMLTemplate(templ) } @@ -322,12 +328,12 @@ func (engine *Engine) HandleContext(c *Context) { engine.pool.Put(c) } -func (engine *Engine) handleHTTPRequest(context *Context) { - httpMethod := context.Request.Method - path := context.Request.URL.Path +func (engine *Engine) handleHTTPRequest(c *Context) { + httpMethod := c.Request.Method + path := c.Request.URL.Path unescape := false - if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 { - path = context.Request.URL.RawPath + if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 { + path = c.Request.URL.RawPath unescape = engine.UnescapePathValues } @@ -337,20 +343,20 @@ func (engine *Engine) handleHTTPRequest(context *Context) { if t[i].method == httpMethod { root := t[i].root // Find route in tree - handlers, params, tsr := root.getValue(path, context.Params, unescape) + handlers, params, tsr := root.getValue(path, c.Params, unescape) if handlers != nil { - context.handlers = handlers - context.Params = params - context.Next() - context.writermem.WriteHeaderNow() + c.handlers = handlers + c.Params = params + c.Next() + c.writermem.WriteHeaderNow() return } if httpMethod != "CONNECT" && path != "/" { if tsr && engine.RedirectTrailingSlash { - redirectTrailingSlash(context) + redirectTrailingSlash(c) return } - if engine.RedirectFixedPath && redirectFixedPath(context, root, engine.RedirectFixedPath) { + if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) { return } } @@ -362,15 +368,15 @@ func (engine *Engine) handleHTTPRequest(context *Context) { for _, tree := range engine.trees { if tree.method != httpMethod { if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil { - context.handlers = engine.allNoMethod - serveError(context, 405, default405Body) + c.handlers = engine.allNoMethod + serveError(c, 405, default405Body) return } } } } - context.handlers = engine.allNoRoute - serveError(context, 404, default404Body) + c.handlers = engine.allNoRoute + serveError(c, 404, default404Body) } var mimePlain = []string{MIMEPlain} From 3b300929e80cede939bd5f0016b6692d20491ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Thu, 28 Sep 2017 11:22:35 -0500 Subject: [PATCH 107/113] Empty string check (#1101) --- auth.go | 4 ++-- gin.go | 2 +- mode.go | 2 +- render/html.go | 4 ++-- utils.go | 11 +++++------ 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/auth.go b/auth.go index 85d96fd3..302a4fad 100644 --- a/auth.go +++ b/auth.go @@ -24,7 +24,7 @@ type authPair struct { type authPairs []authPair func (a authPairs) searchCredential(authValue string) (string, bool) { - if len(authValue) == 0 { + if authValue == "" { return "", false } for _, pair := range a { @@ -71,7 +71,7 @@ func processAccounts(accounts Accounts) authPairs { assert1(len(accounts) > 0, "Empty list of authorized credentials") pairs := make(authPairs, 0, len(accounts)) for user, password := range accounts { - assert1(len(user) > 0, "User can not be empty") + assert1(user != "", "User can not be empty") value := authorizationHeader(user, password) pairs = append(pairs, authPair{ Value: value, diff --git a/gin.go b/gin.go index 3a51bc14..9466e074 100644 --- a/gin.go +++ b/gin.go @@ -231,7 +231,7 @@ func (engine *Engine) rebuild405Handlers() { func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { assert1(path[0] == '/', "path must begin with '/'") - assert1(len(method) > 0, "HTTP method can not be empty") + assert1(method != "", "HTTP method can not be empty") assert1(len(handlers) > 0, "there must be at least one handler") debugPrintRoute(method, path, handlers) diff --git a/mode.go b/mode.go index b0d2c27d..393e13ec 100644 --- a/mode.go +++ b/mode.go @@ -39,7 +39,7 @@ var modeName = DebugMode func init() { mode := os.Getenv(ENV_GIN_MODE) - if len(mode) == 0 { + if mode == "" { SetMode(DebugMode) } else { SetMode(mode) diff --git a/render/html.go b/render/html.go index 332d3ba2..1e3be65b 100644 --- a/render/html.go +++ b/render/html.go @@ -60,7 +60,7 @@ func (r HTMLDebug) loadTemplate() *template.Template { if len(r.Files) > 0 { return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...)) } - if len(r.Glob) > 0 { + 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") @@ -69,7 +69,7 @@ func (r HTMLDebug) loadTemplate() *template.Template { func (r HTML) Render(w http.ResponseWriter) error { r.WriteContentType(w) - if len(r.Name) == 0 { + if r.Name == "" { return r.Template.Execute(w, r.Data) } return r.Template.ExecuteTemplate(w, r.Name, r.Data) diff --git a/utils.go b/utils.go index ab06a759..e909bb70 100644 --- a/utils.go +++ b/utils.go @@ -103,7 +103,7 @@ func parseAccept(acceptHeader string) []string { if index := strings.IndexByte(part, ';'); index >= 0 { part = part[0:index] } - if part = strings.TrimSpace(part); len(part) > 0 { + if part = strings.TrimSpace(part); part != "" { out = append(out, part) } } @@ -111,11 +111,10 @@ func parseAccept(acceptHeader string) []string { } func lastChar(str string) uint8 { - size := len(str) - if size == 0 { + if str == "" { panic("The length of the string can't be 0") } - return str[size-1] + return str[len(str)-1] } func nameOfFunction(f interface{}) string { @@ -123,7 +122,7 @@ func nameOfFunction(f interface{}) string { } func joinPaths(absolutePath, relativePath string) string { - if len(relativePath) == 0 { + if relativePath == "" { return absolutePath } @@ -138,7 +137,7 @@ func joinPaths(absolutePath, relativePath string) string { func resolveAddress(addr []string) string { switch len(addr) { case 0: - if port := os.Getenv("PORT"); len(port) > 0 { + if port := os.Getenv("PORT"); port != "" { debugPrint("Environment variable PORT=\"%s\"", port) return ":" + port } From 0cb7c44abc8280411ee5ab3cc67ff63aaee2bf3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Thu, 28 Sep 2017 22:58:57 -0500 Subject: [PATCH 108/113] Print warning log when user create one engine using gin.Default in debug mode (#1121) * empty string check * add log when use Default * fix unit test error * fix unit test error --- debug.go | 6 ++++++ debug_test.go | 18 ++++++++++++++++++ gin.go | 1 + 3 files changed, 25 insertions(+) diff --git a/debug.go b/debug.go index b31ca685..449291e6 100644 --- a/debug.go +++ b/debug.go @@ -46,6 +46,12 @@ func debugPrint(format string, values ...interface{}) { } } +func debugPrintWARNINGDefault() { + debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. + +`) +} + func debugPrintWARNINGNew() { debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release diff --git a/debug_test.go b/debug_test.go index 366d4613..dfd54c82 100644 --- a/debug_test.go +++ b/debug_test.go @@ -86,6 +86,24 @@ func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) { assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", w.String()) } +func TestDebugPrintWARNINGDefault(t *testing.T) { + var w bytes.Buffer + setup(&w) + defer teardown() + + debugPrintWARNINGDefault() + assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", w.String()) +} + +func TestDebugPrintWARNINGNew(t *testing.T) { + var w bytes.Buffer + setup(&w) + defer teardown() + + debugPrintWARNINGNew() + assert.Equal(t, "[GIN-debug] [WARNING] Running in \"debug\" mode. Switch to \"release\" mode in production.\n - using env:\texport GIN_MODE=release\n - using code:\tgin.SetMode(gin.ReleaseMode)\n\n", w.String()) +} + func setup(w io.Writer) { SetMode(DebugMode) log.SetOutput(w) diff --git a/gin.go b/gin.go index 9466e074..8cfb1f13 100644 --- a/gin.go +++ b/gin.go @@ -144,6 +144,7 @@ func New() *Engine { // Default returns an Engine instance with the Logger and Recovery middleware already attached. func Default() *Engine { + debugPrintWARNINGDefault() engine := New() engine.Use(Logger(), Recovery()) return engine From b8b68314faa067acae5b17c828127b04af51ebe2 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 29 Sep 2017 16:48:10 +0800 Subject: [PATCH 109/113] feat: add multiple service example. (#1119) --- README.md | 82 +++++++++++++++++++++++++++++++ examples/multiple-service/main.go | 74 ++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 examples/multiple-service/main.go diff --git a/README.md b/README.md index 0dc165bc..66c83fb1 100644 --- a/README.md +++ b/README.md @@ -1154,6 +1154,88 @@ func main() { } ``` +### Run multiple service using Gin + +See the [question](https://github.com/gin-gonic/gin/issues/346) and try the folling example: + +[embedmd]:# (examples/multiple-service/main.go go) +```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 { + return server01.ListenAndServe() + }) + + g.Go(func() error { + return server02.ListenAndServe() + }) + + if err := g.Wait(); err != nil { + log.Fatal(err) + } +} +``` + ### Graceful restart or stop Do you want to graceful restart or stop your web server? diff --git a/examples/multiple-service/main.go b/examples/multiple-service/main.go new file mode 100644 index 00000000..ceddaa2e --- /dev/null +++ b/examples/multiple-service/main.go @@ -0,0 +1,74 @@ +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 { + return server01.ListenAndServe() + }) + + g.Go(func() error { + return server02.ListenAndServe() + }) + + if err := g.Wait(); err != nil { + log.Fatal(err) + } +} From dfb68ce0852d3fe600148f486f67d2327146dfea Mon Sep 17 00:00:00 2001 From: Suhas Karanth Date: Mon, 23 Oct 2017 14:44:09 +0530 Subject: [PATCH 110/113] feat(context): ShouldBind counterparts for Bind methods (#1047) * feat(context): ShouldBind counterparts for Bind methods + tests * docs(readme): Switch examples to use ShouldBind methods Add section for bind methods types, explain difference in behavior. Switch all `c.Bind` examples to use `c.ShouldBind`. --- README.md | 62 ++++++++++++++++++++++++++++++++++----------- context.go | 23 +++++++++++++++++ context_test.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 66c83fb1..7113058d 100644 --- a/README.md +++ b/README.md @@ -388,7 +388,7 @@ func main() { // Logger middleware will write the logs to gin.DefaultWriter even 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()) @@ -422,11 +422,11 @@ func main() { 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) @@ -447,9 +447,17 @@ Gin uses [**go-playground/validator.v8**](https://github.com/go-playground/valid 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"`. -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 BindWith. +Also, Gin provides two sets of methods for binding: +- **Type** - Must bind + - **Methods** - `Bind`, `BindJSON`, `BindQuery` + - **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`, `ShouldBindQuery` + - **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. -You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has a empty value when binding, the current request will fail with an error. +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. ```go // Binding from JSON @@ -464,12 +472,14 @@ func main() { // Example for binding JSON ({"user": "manu", "password": "123"}) router.POST("/loginJSON", func(c *gin.Context) { var json Login - if c.BindJSON(&json) == nil { + if err = c.ShouldBindJSON(&json); err == nil { if json.User == "manu" && json.Password == "123" { c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) } else { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) } + } else { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } }) @@ -477,12 +487,14 @@ func main() { router.POST("/loginForm", func(c *gin.Context) { var form Login // This will infer what binder to use depending on the content-type header. - if c.Bind(&form) == nil { + if err := c.ShouldBind(&form); err == nil { if form.User == "manu" && form.Password == "123" { c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) } else { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) } + } else { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } }) @@ -491,6 +503,28 @@ func main() { } ``` +**Sample request** +```shell +$ 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"} +``` + ### Custom Validators It is also possible to register custom validators. See the [example code](examples/custom-validation/server.go). @@ -554,7 +588,7 @@ $ curl "localhost:8085/bookable?check_in=2017-08-15&check_out=2017-08-16" ### Only Bind Query String -`BindQuery` 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). +`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 @@ -578,7 +612,7 @@ func main() { func startPage(c *gin.Context) { var person Person - if c.BindQuery(&person) == nil { + if c.ShouldBindQuery(&person) == nil { log.Println("====== Only Bind By Query String ======") log.Println(person.Name) log.Println(person.Address) @@ -616,7 +650,7 @@ 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#L48 - if c.Bind(&person) == nil { + if c.ShouldBind(&person) == nil { log.Println(person.Name) log.Println(person.Address) log.Println(person.Birthday) @@ -648,7 +682,7 @@ type myForm struct { func formHandler(c *gin.Context) { var fakeForm myForm - c.Bind(&fakeForm) + c.ShouldBind(&fakeForm) c.JSON(200, gin.H{"color": fakeForm.Colors}) } @@ -695,11 +729,11 @@ func main() { router := gin.Default() router.POST("/login", func(c *gin.Context) { // you can bind multipart form with explicit binding declaration: - // c.MustBindWith(&form, binding.Form) - // or you can simply use autobinding with Bind method: + // c.ShouldBindWith(&form, binding.Form) + // or you can simply use autobinding with ShouldBind method: var form LoginForm // in this case proper binding will be automatically selected - if c.Bind(&form) == nil { + if c.ShouldBind(&form) == nil { if form.User == "user" && form.Password == "password" { c.JSON(200, gin.H{"status": "you are logged in"}) } else { diff --git a/context.go b/context.go index 74995e68..5b67dcc0 100644 --- a/context.go +++ b/context.go @@ -482,6 +482,29 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) { return } +// ShouldBind checks the Content-Type to select a binding engine automatically, +// Depending the "Content-Type" header different bindings are used: +// "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. +func (c *Context) ShouldBind(obj interface{}) 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 { + return c.ShouldBindWith(obj, binding.JSON) +} + +// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query). +func (c *Context) ShouldBindQuery(obj interface{}) error { + return c.ShouldBindWith(obj, binding.Query) +} + // 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 { diff --git a/context_test.go b/context_test.go index 4854a8a8..8cd05f6c 100644 --- a/context_test.go +++ b/context_test.go @@ -1228,6 +1228,73 @@ func TestContextBadAutoBind(t *testing.T) { assert.True(t, c.IsAborted()) } +func TestContextAutoShouldBindJSON(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request.Header.Add("Content-Type", MIMEJSON) + + var obj struct { + Foo string `json:"foo"` + Bar string `json:"bar"` + } + assert.NoError(t, c.ShouldBind(&obj)) + assert.Equal(t, obj.Bar, "foo") + assert.Equal(t, obj.Foo, "bar") + assert.Empty(t, c.Errors) +} + +func TestContextShouldBindWithJSON(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type + + var obj struct { + Foo string `json:"foo"` + Bar string `json:"bar"` + } + assert.NoError(t, c.ShouldBindJSON(&obj)) + assert.Equal(t, obj.Bar, "foo") + assert.Equal(t, obj.Foo, "bar") + assert.Equal(t, w.Body.Len(), 0) +} + +func TestContextShouldBindWithQuery(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) + + var obj struct { + Foo string `form:"foo"` + Bar string `form:"bar"` + } + assert.NoError(t, c.ShouldBindQuery(&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) + + c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}")) + c.Request.Header.Add("Content-Type", MIMEJSON) + var obj struct { + Foo string `json:"foo"` + Bar string `json:"bar"` + } + + assert.False(t, c.IsAborted()) + assert.Error(t, c.ShouldBind(&obj)) + + assert.Empty(t, obj.Bar) + assert.Empty(t, obj.Foo) + assert.False(t, c.IsAborted()) +} + func TestContextGolangContext(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) From 1e88466d234a82ce4aeca904bce8a0b93fac3d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Thu, 26 Oct 2017 10:25:25 +0800 Subject: [PATCH 111/113] Update the comment of Version (#1141) --- gin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 8cfb1f13..4857f3ae 100644 --- a/gin.go +++ b/gin.go @@ -14,8 +14,8 @@ import ( "github.com/gin-gonic/gin/render" ) -// Version is Framework's version. const ( + // Version is Framework's version. Version = "v1.2" defaultMultipartMemory = 32 << 20 // 32 MB ) From b7e8a6b9b062473c7f3f4f5c16d0a28b6244de48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Sun, 29 Oct 2017 20:12:22 +0800 Subject: [PATCH 112/113] style(import): not use aliase when import package (#1146) --- binding/binding.go | 2 +- binding/json.go | 4 +--- binding/query.go | 4 +--- binding/validate_test.go | 3 +-- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/binding/binding.go b/binding/binding.go index a09cc221..dc32d538 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -7,7 +7,7 @@ package binding import ( "net/http" - validator "gopkg.in/go-playground/validator.v8" + "gopkg.in/go-playground/validator.v8" ) const ( diff --git a/binding/json.go b/binding/json.go index f600a5f4..b7c856af 100644 --- a/binding/json.go +++ b/binding/json.go @@ -10,9 +10,7 @@ import ( "github.com/gin-gonic/gin/json" ) -var ( - EnableDecoderUseNumber = false -) +var EnableDecoderUseNumber = false type jsonBinding struct{} diff --git a/binding/query.go b/binding/query.go index a789f798..219743f2 100644 --- a/binding/query.go +++ b/binding/query.go @@ -4,9 +4,7 @@ package binding -import ( - "net/http" -) +import "net/http" type queryBinding struct{} diff --git a/binding/validate_test.go b/binding/validate_test.go index 523e1298..8ca79989 100644 --- a/binding/validate_test.go +++ b/binding/validate_test.go @@ -10,9 +10,8 @@ import ( "testing" "time" - validator "gopkg.in/go-playground/validator.v8" - "github.com/stretchr/testify/assert" + "gopkg.in/go-playground/validator.v8" ) type testInterface interface { From 6653d5d588740eb73d2245ef04d5a407d3c3f034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Mon, 30 Oct 2017 10:38:38 +0800 Subject: [PATCH 113/113] fix markdown render (#1149) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7113058d..b7181036 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,10 @@ BenchmarkTigerTonic_GithubAll | 1000 | 1439483 | 239104 BenchmarkTraffic_GithubAll | 100 | 11383067 | 2659329 | 21848 BenchmarkVulcan_GithubAll | 5000 | 394253 | 19894 | 609 -(1): Total Repetitions achieved in constant time, higher means more confident result -(2): Single Repetition Duration (ns/op), lower is better -(3): Heap Memory (B/op), lower is better -(4): Average Allocations per Repetition (allocs/op), lower is better +- (1): Total Repetitions achieved in constant time, higher means more confident result +- (2): Single Repetition Duration (ns/op), lower is better +- (3): Heap Memory (B/op), lower is better +- (4): Average Allocations per Repetition (allocs/op), lower is better ## Gin v1. stable