From 0b96dd8ae554b8131de6b354e300ee6cf8e56f69 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 22 Sep 2019 15:35:34 +0800 Subject: [PATCH 01/90] chore: remove env var for go master branch (#2056) --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8b3b5a29..748a07a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,6 @@ matrix: env: GO111MODULE=on - go: 1.13.x - go: master - env: GO111MODULE=on git: depth: 10 From f45c83c70cb27a16d3cae220afff2a731ea4f707 Mon Sep 17 00:00:00 2001 From: bullgare Date: Mon, 23 Sep 2019 18:48:10 +0300 Subject: [PATCH 02/90] Updated Readme.md for serving multiple services (#2067) Previous version had issues - if one service did not start for any reason, you would never know about it. --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b488f159..dfedd8a2 100644 --- a/README.md +++ b/README.md @@ -1678,11 +1678,19 @@ func main() { } g.Go(func() error { - return server01.ListenAndServe() + err := server01.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatal(err) + } + return err }) g.Go(func() error { - return server02.ListenAndServe() + err := server02.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatal(err) + } + return err }) if err := g.Wait(); err != nil { From 2e5a7196cce94e085625d166e7f2cf9f99a1edf0 Mon Sep 17 00:00:00 2001 From: Santhosh Kumar Date: Tue, 24 Sep 2019 07:31:57 +0530 Subject: [PATCH 03/90] use url.URL.Query instead of parsing query (#2063) --- context.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/context.go b/context.go index 86094cac..509ce081 100644 --- a/context.go +++ b/context.go @@ -393,8 +393,7 @@ func (c *Context) QueryArray(key string) []string { func (c *Context) getQueryCache() { if c.queryCache == nil { - c.queryCache = make(url.Values) - c.queryCache, _ = url.ParseQuery(c.Request.URL.RawQuery) + c.queryCache = c.Request.URL.Query() } } From d6eafcf48abbf3895df5ae5019682b7ae1f1ca2e Mon Sep 17 00:00:00 2001 From: Gaozhen Ying Date: Tue, 24 Sep 2019 21:44:15 +0800 Subject: [PATCH 04/90] add TestDisableBindValidation (#2071) --- mode_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mode_test.go b/mode_test.go index 0c5a3234..1b5fb2ff 100644 --- a/mode_test.go +++ b/mode_test.go @@ -40,6 +40,14 @@ func TestSetMode(t *testing.T) { assert.Panics(t, func() { SetMode("unknown") }) } +func TestDisableBindValidation(t *testing.T) { + v := binding.Validator + assert.NotNil(t, binding.Validator) + DisableBindValidation() + assert.Nil(t, binding.Validator) + binding.Validator = v +} + func TestEnableJsonDecoderUseNumber(t *testing.T) { assert.False(t, binding.EnableDecoderUseNumber) EnableJsonDecoderUseNumber() From 9b9f4fab34cc3e47e3c7e390d2e2d9c11276d9b3 Mon Sep 17 00:00:00 2001 From: bullgare Date: Tue, 24 Sep 2019 17:18:41 +0300 Subject: [PATCH 05/90] Updated Readme.md: file.Close() for template read (#2068) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dfedd8a2..959848dd 100644 --- a/README.md +++ b/README.md @@ -1807,6 +1807,7 @@ func main() { func loadTemplate() (*template.Template, error) { t := template.New("") for name, file := range Assets.Files { + defer file.Close() if file.IsDir() || !strings.HasSuffix(name, ".tmpl") { continue } From 79840bc1c62d7d6104e2b1c5d39099f92f9f8d11 Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Mon, 30 Sep 2019 09:12:22 +0800 Subject: [PATCH 06/90] support run HTTP server with specific net.Listener (#2023) --- gin.go | 9 +++++++++ gin_integration_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/gin.go b/gin.go index cbdd080e..894cf094 100644 --- a/gin.go +++ b/gin.go @@ -338,6 +338,15 @@ func (engine *Engine) RunFd(fd int) (err error) { return } defer listener.Close() + err = engine.RunListener(listener) + return +} + +// RunListener attaches the router to a http.Server and starts listening and serving HTTP requests +// through the specified net.Listener +func (engine *Engine) RunListener(listener net.Listener) (err error) { + debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr()) + defer func() { debugPrintError(err) }() err = http.Serve(listener, engine) return } diff --git a/gin_integration_test.go b/gin_integration_test.go index 9beec14d..7e270b91 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -207,6 +207,42 @@ func TestBadFileDescriptor(t *testing.T) { assert.Error(t, router.RunFd(0)) } +func TestListener(t *testing.T) { + router := New() + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + assert.NoError(t, err) + listener, err := net.ListenTCP("tcp", addr) + assert.NoError(t, err) + go func() { + router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) + assert.NoError(t, router.RunListener(listener)) + }() + // have to wait for the goroutine to start and run the server + // otherwise the main thread will complete + time.Sleep(5 * time.Millisecond) + + c, err := net.Dial("tcp", listener.Addr().String()) + assert.NoError(t, err) + + fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n") + scanner := bufio.NewScanner(c) + var response string + for scanner.Scan() { + response += scanner.Text() + } + assert.Contains(t, response, "HTTP/1.0 200", "should get a 200") + assert.Contains(t, response, "it worked", "resp body should match") +} + +func TestBadListener(t *testing.T) { + router := New() + addr, err := net.ResolveTCPAddr("tcp", "localhost:10086") + assert.NoError(t, err) + listener, err := net.ListenTCP("tcp", addr) + listener.Close() + assert.Error(t, router.RunListener(listener)) +} + func TestWithHttptestWithAutoSelectedPort(t *testing.T) { router := New() router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) From beb879e4754af8d25b9f326c20d831e518ad645f Mon Sep 17 00:00:00 2001 From: John Bampton Date: Mon, 30 Sep 2019 16:22:12 +1000 Subject: [PATCH 07/90] Change Writter to Writer. (#2079) --- response_writer_test.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/response_writer_test.go b/response_writer_test.go index a5e111e5..1f113e74 100644 --- a/response_writer_test.go +++ b/response_writer_test.go @@ -29,38 +29,38 @@ func init() { } func TestResponseWriterReset(t *testing.T) { - testWritter := httptest.NewRecorder() + testWriter := httptest.NewRecorder() writer := &responseWriter{} var w ResponseWriter = writer - writer.reset(testWritter) + writer.reset(testWriter) assert.Equal(t, -1, writer.size) assert.Equal(t, http.StatusOK, writer.status) - assert.Equal(t, testWritter, writer.ResponseWriter) + assert.Equal(t, testWriter, writer.ResponseWriter) assert.Equal(t, -1, w.Size()) assert.Equal(t, http.StatusOK, w.Status()) assert.False(t, w.Written()) } func TestResponseWriterWriteHeader(t *testing.T) { - testWritter := httptest.NewRecorder() + testWriter := httptest.NewRecorder() writer := &responseWriter{} - writer.reset(testWritter) + writer.reset(testWriter) w := ResponseWriter(writer) w.WriteHeader(http.StatusMultipleChoices) assert.False(t, w.Written()) assert.Equal(t, http.StatusMultipleChoices, w.Status()) - assert.NotEqual(t, http.StatusMultipleChoices, testWritter.Code) + assert.NotEqual(t, http.StatusMultipleChoices, testWriter.Code) w.WriteHeader(-1) assert.Equal(t, http.StatusMultipleChoices, w.Status()) } func TestResponseWriterWriteHeadersNow(t *testing.T) { - testWritter := httptest.NewRecorder() + testWriter := httptest.NewRecorder() writer := &responseWriter{} - writer.reset(testWritter) + writer.reset(testWriter) w := ResponseWriter(writer) w.WriteHeader(http.StatusMultipleChoices) @@ -68,7 +68,7 @@ func TestResponseWriterWriteHeadersNow(t *testing.T) { assert.True(t, w.Written()) assert.Equal(t, 0, w.Size()) - assert.Equal(t, http.StatusMultipleChoices, testWritter.Code) + assert.Equal(t, http.StatusMultipleChoices, testWriter.Code) writer.size = 10 w.WriteHeaderNow() @@ -76,30 +76,30 @@ func TestResponseWriterWriteHeadersNow(t *testing.T) { } func TestResponseWriterWrite(t *testing.T) { - testWritter := httptest.NewRecorder() + testWriter := httptest.NewRecorder() writer := &responseWriter{} - writer.reset(testWritter) + writer.reset(testWriter) w := ResponseWriter(writer) n, err := w.Write([]byte("hola")) assert.Equal(t, 4, n) assert.Equal(t, 4, w.Size()) assert.Equal(t, http.StatusOK, w.Status()) - assert.Equal(t, http.StatusOK, testWritter.Code) - assert.Equal(t, "hola", testWritter.Body.String()) + assert.Equal(t, http.StatusOK, testWriter.Code) + assert.Equal(t, "hola", testWriter.Body.String()) assert.NoError(t, err) n, err = w.Write([]byte(" adios")) assert.Equal(t, 6, n) assert.Equal(t, 10, w.Size()) - assert.Equal(t, "hola adios", testWritter.Body.String()) + assert.Equal(t, "hola adios", testWriter.Body.String()) assert.NoError(t, err) } func TestResponseWriterHijack(t *testing.T) { - testWritter := httptest.NewRecorder() + testWriter := httptest.NewRecorder() writer := &responseWriter{} - writer.reset(testWritter) + writer.reset(testWriter) w := ResponseWriter(writer) assert.Panics(t, func() { From 4fd3234840dbfec7b619f70f341e339f66604cfd Mon Sep 17 00:00:00 2001 From: John Bampton Date: Thu, 3 Oct 2019 09:46:41 +1000 Subject: [PATCH 08/90] Fix spelling. (#2080) --- CHANGELOG.md | 6 +++--- README.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15dfb1a8..6ccd2faf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ - [NEW] Refactor form mappings [#1749](https://github.com/gin-gonic/gin/pull/1749) - [NEW] Added flag to context.Stream indicates if client disconnected in middle of stream [#1252](https://github.com/gin-gonic/gin/pull/1252) - [FIX] Moved [examples](https://github.com/gin-gonic/examples) to stand alone Repo [#1775](https://github.com/gin-gonic/gin/pull/1775) -- [NEW] Extend context.File to allow for the content-dispositon attachments via a new method context.Attachment [#1260](https://github.com/gin-gonic/gin/pull/1260) +- [NEW] Extend context.File to allow for the content-disposition attachments via a new method context.Attachment [#1260](https://github.com/gin-gonic/gin/pull/1260) - [FIX] Support HTTP content negotiation wildcards [#1112](https://github.com/gin-gonic/gin/pull/1112) - [NEW] Add prefix from X-Forwarded-Prefix in redirectTrailingSlash [#1238](https://github.com/gin-gonic/gin/pull/1238) - [FIX] context.Copy() race condition [#1020](https://github.com/gin-gonic/gin/pull/1020) @@ -231,7 +231,7 @@ - [PERFORMANCE] Improve context's memory locality, reduce CPU cache faults. - [NEW] Flexible rendering API - [NEW] Add Context.File() -- [NEW] Add shorcut RunTLS() for http.ListenAndServeTLS +- [NEW] Add shortcut RunTLS() for http.ListenAndServeTLS - [FIX] Rename NotFound404() to NoRoute() - [FIX] Errors in context are purged - [FIX] Adds HEAD method in Static file serving @@ -254,7 +254,7 @@ - [NEW] New Bind() and BindWith() methods for parsing request body. - [NEW] Add Content.Copy() - [NEW] Add context.LastError() -- [NEW] Add shorcut for OPTIONS HTTP method +- [NEW] Add shortcut for OPTIONS HTTP method - [FIX] Tons of README fixes - [FIX] Header is written before body - [FIX] BasicAuth() and changes API a little bit diff --git a/README.md b/README.md index 959848dd..22f83b65 100644 --- a/README.md +++ b/README.md @@ -1149,7 +1149,7 @@ func main() { #### AsciiJSON -Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII chracters. +Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII characters. ```go func main() { From f7becac7bc7290c23174ebbaf510db545660bb8e Mon Sep 17 00:00:00 2001 From: Dmitry Kutakov Date: Thu, 10 Oct 2019 10:58:31 +0200 Subject: [PATCH 09/90] Relocate binding body tests (#2086) * Relocate binding body tests Every test file should be related to a tested file. Remove useless tests. * Add github.com/stretchr/testify/require package --- binding/binding_body_test.go | 72 ------------------------------------ binding/json_test.go | 21 +++++++++++ binding/msgpack_test.go | 32 ++++++++++++++++ binding/xml_test.go | 25 +++++++++++++ binding/yaml_test.go | 21 +++++++++++ vendor/vendor.json | 6 +++ 6 files changed, 105 insertions(+), 72 deletions(-) delete mode 100644 binding/binding_body_test.go create mode 100644 binding/json_test.go create mode 100644 binding/msgpack_test.go create mode 100644 binding/xml_test.go create mode 100644 binding/yaml_test.go diff --git a/binding/binding_body_test.go b/binding/binding_body_test.go deleted file mode 100644 index 901d429c..00000000 --- a/binding/binding_body_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package binding - -import ( - "bytes" - "io/ioutil" - "testing" - - "github.com/gin-gonic/gin/testdata/protoexample" - "github.com/golang/protobuf/proto" - "github.com/stretchr/testify/assert" - "github.com/ugorji/go/codec" -) - -func TestBindingBody(t *testing.T) { - for _, tt := range []struct { - name string - binding BindingBody - body string - want string - }{ - { - name: "JSON binding", - binding: JSON, - body: `{"foo":"FOO"}`, - }, - { - name: "XML binding", - binding: XML, - body: ` - - FOO -`, - }, - { - name: "MsgPack binding", - binding: MsgPack, - body: msgPackBody(t), - }, - { - name: "YAML binding", - binding: YAML, - body: `foo: FOO`, - }, - } { - t.Logf("testing: %s", tt.name) - req := requestWithBody("POST", "/", tt.body) - form := FooStruct{} - body, _ := ioutil.ReadAll(req.Body) - assert.NoError(t, tt.binding.BindBody(body, &form)) - assert.Equal(t, FooStruct{"FOO"}, form) - } -} - -func msgPackBody(t *testing.T) string { - test := FooStruct{"FOO"} - h := new(codec.MsgpackHandle) - buf := bytes.NewBuffer(nil) - assert.NoError(t, codec.NewEncoder(buf, h).Encode(test)) - return buf.String() -} - -func TestBindingBodyProto(t *testing.T) { - test := protoexample.Test{ - Label: proto.String("FOO"), - } - data, _ := proto.Marshal(&test) - req := requestWithBody("POST", "/", string(data)) - form := protoexample.Test{} - body, _ := ioutil.ReadAll(req.Body) - assert.NoError(t, ProtoBuf.BindBody(body, &form)) - assert.Equal(t, test, form) -} diff --git a/binding/json_test.go b/binding/json_test.go new file mode 100644 index 00000000..cae4cccc --- /dev/null +++ b/binding/json_test.go @@ -0,0 +1,21 @@ +// Copyright 2019 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestJSONBindingBindBody(t *testing.T) { + var s struct { + Foo string `json:"foo"` + } + err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO"}`), &s) + require.NoError(t, err) + assert.Equal(t, "FOO", s.Foo) +} diff --git a/binding/msgpack_test.go b/binding/msgpack_test.go new file mode 100644 index 00000000..6baa6739 --- /dev/null +++ b/binding/msgpack_test.go @@ -0,0 +1,32 @@ +// Copyright 2019 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/ugorji/go/codec" +) + +func TestMsgpackBindingBindBody(t *testing.T) { + type teststruct struct { + Foo string `msgpack:"foo"` + } + var s teststruct + err := msgpackBinding{}.BindBody(msgpackBody(t, teststruct{"FOO"}), &s) + require.NoError(t, err) + assert.Equal(t, "FOO", s.Foo) +} + +func msgpackBody(t *testing.T, obj interface{}) []byte { + var bs bytes.Buffer + h := &codec.MsgpackHandle{} + err := codec.NewEncoder(&bs, h).Encode(obj) + require.NoError(t, err) + return bs.Bytes() +} diff --git a/binding/xml_test.go b/binding/xml_test.go new file mode 100644 index 00000000..f9546c1a --- /dev/null +++ b/binding/xml_test.go @@ -0,0 +1,25 @@ +// Copyright 2019 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestXMLBindingBindBody(t *testing.T) { + var s struct { + Foo string `xml:"foo"` + } + xmlBody := ` + + FOO +` + err := xmlBinding{}.BindBody([]byte(xmlBody), &s) + require.NoError(t, err) + assert.Equal(t, "FOO", s.Foo) +} diff --git a/binding/yaml_test.go b/binding/yaml_test.go new file mode 100644 index 00000000..e66338b7 --- /dev/null +++ b/binding/yaml_test.go @@ -0,0 +1,21 @@ +// Copyright 2019 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestYAMLBindingBindBody(t *testing.T) { + var s struct { + Foo string `yaml:"foo"` + } + err := yamlBinding{}.BindBody([]byte("foo: FOO"), &s) + require.NoError(t, err) + assert.Equal(t, "FOO", s.Foo) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index d441d4a6..70b2d9eb 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -112,6 +112,12 @@ "version": "v1.2", "versionExact": "v1.2.2" }, + { + "checksumSHA1": "wnEANt4k5X/KGwoFyfSSnpxULm4=", + "path": "github.com/stretchr/testify/require", + "revision": "f35b8ab0b5a2cef36673838d662e249dd9c94686", + "revisionTime": "2018-05-06T18:05:49Z" + }, { "checksumSHA1": "S4ei9eSqVThDio0Jn2sav6yUbvg=", "path": "github.com/ugorji/go/codec", From 3cea16cc6c9391224d122fe303b0dc81454acbd2 Mon Sep 17 00:00:00 2001 From: Dmitry Kutakov Date: Tue, 15 Oct 2019 05:04:25 +0200 Subject: [PATCH 10/90] Update go.sum file (#2094) --- go.sum | 1 + 1 file changed, 1 insertion(+) diff --git a/go.sum b/go.sum index 7b4ee320..129ad387 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= From 1a1cf655bd72f769e680270f2d39e43f1b82b2ad Mon Sep 17 00:00:00 2001 From: Dmitry Kutakov Date: Tue, 15 Oct 2019 08:25:55 +0200 Subject: [PATCH 11/90] add details in issue template (#2085) indirectly request more details --- .github/ISSUE_TEMPLATE.md | 46 ++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 9d49aa41..6f8288d5 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,11 +3,47 @@ - Please provide source code and commit sha if you found a bug. - Review existing issues and provide feedback or react to them. +## Description + + + +## How to reproduce + + +``` +package main + +import ( + "github.com/gin-gonic/gin" +) + +func main() { + g := gin.Default() + g.GET("/hello/:name", func(c *gin.Context) { + c.String(200, "Hello %s", c.Param("name")) + }) + g.Run(":9000") +} +``` + +## Expectations + + +``` +$ curl http://localhost:8201/hello/world +Hello world +``` + +## Actual result + + +``` +$ curl -i http://localhost:8201/hello/world + +``` + +## Environment + - go version: - gin version (or commit ref): - operating system: - -## Description - -## Screenshots - From 0ce46610292cc8877a914b1cee41acd5dc9da7ae Mon Sep 17 00:00:00 2001 From: willnewrelic Date: Wed, 16 Oct 2019 19:14:44 -0700 Subject: [PATCH 12/90] Use Writer in Context.Status (#1606) --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 509ce081..18d328d2 100644 --- a/context.go +++ b/context.go @@ -744,7 +744,7 @@ func bodyAllowedForStatus(status int) bool { // Status sets the HTTP response code. func (c *Context) Status(code int) { - c.writermem.WriteHeader(code) + c.Writer.WriteHeader(code) } // Header is a intelligent shortcut for c.Writer.Header().Set(key, value). From 089016a09297b7acc16b3df2ef2add1880b17502 Mon Sep 17 00:00:00 2001 From: Ildar1111 <54001462+Ildar1111@users.noreply.github.com> Date: Fri, 25 Oct 2019 05:03:53 +0300 Subject: [PATCH 13/90] Update README.md (#2106) * Update README.md c:\>curl 0.0.0.0:8080 "Failed to connect to 0.0.0.0 port 8080: Address not available" Connecting to address 0.0.0.0:8080 is not allowed on windows. From http://msdn.microsoft.com/en-us/library/aa923167.aspx " ... If the address member of the structure specified by the name parameter is all zeroes, connect will return the error WSAEADDRNOTAVAIL. ..." * Update README.md edit comment --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 22f83b65..3dd99d99 100644 --- a/README.md +++ b/README.md @@ -145,12 +145,12 @@ func main() { "message": "pong", }) }) - r.Run() // listen and serve on 0.0.0.0:8080 + r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") } ``` ``` -# run example.go and visit 0.0.0.0:8080/ping on browser +# run example.go and visit 0.0.0.0:8080/ping (for windows "localhost:8080/ping") on browser $ go run example.go ``` From 8a1bfcfd3b8b514f5cd9d36b575af204a86ddfb0 Mon Sep 17 00:00:00 2001 From: ZhangYunHao Date: Sat, 26 Oct 2019 14:20:35 +0800 Subject: [PATCH 14/90] format errUnknownType (#2103) --- binding/form_mapping.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 80b1d15a..ec78bfee 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -15,7 +15,7 @@ import ( "github.com/gin-gonic/gin/internal/json" ) -var errUnknownType = errors.New("Unknown type") +var errUnknownType = errors.New("unknown type") func mapUri(ptr interface{}, m map[string][]string) error { return mapFormByTag(ptr, m, "uri") From 393a63f3b020df89d42695064443760c7d0a0dc8 Mon Sep 17 00:00:00 2001 From: Dmitry Kutakov Date: Sun, 27 Oct 2019 06:58:59 +0100 Subject: [PATCH 15/90] Fix 'errcheck' linter warnings (#2093) --- binding/binding_test.go | 9 ++++++--- binding/form_mapping_benchmark_test.go | 10 ++++++++-- gin.go | 5 ++++- gin_integration_test.go | 4 +++- render/render_test.go | 5 ++++- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/binding/binding_test.go b/binding/binding_test.go index caabaace..f0b6f795 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -441,7 +441,8 @@ func createFormFilesMultipartRequest(t *testing.T) *http.Request { defer f.Close() fw, err1 := mw.CreateFormFile("file", "form.go") assert.NoError(t, err1) - io.Copy(fw, f) + _, err = io.Copy(fw, f) + assert.NoError(t, err) req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) assert.NoError(t, err2) @@ -465,7 +466,8 @@ func createFormFilesMultipartRequestFail(t *testing.T) *http.Request { defer f.Close() fw, err1 := mw.CreateFormFile("file_foo", "form_foo.go") assert.NoError(t, err1) - io.Copy(fw, f) + _, err = io.Copy(fw, f) + assert.NoError(t, err) req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) assert.NoError(t, err2) @@ -554,7 +556,8 @@ func TestBindingFormPostForMapFail(t *testing.T) { func TestBindingFormFilesMultipart(t *testing.T) { req := createFormFilesMultipartRequest(t) var obj FooBarFileStruct - FormMultipart.Bind(req, &obj) + err := FormMultipart.Bind(req, &obj) + assert.NoError(t, err) // file from os f, _ := os.Open("form.go") diff --git a/binding/form_mapping_benchmark_test.go b/binding/form_mapping_benchmark_test.go index 0ef08f00..9572ea03 100644 --- a/binding/form_mapping_benchmark_test.go +++ b/binding/form_mapping_benchmark_test.go @@ -32,7 +32,10 @@ type structFull struct { func BenchmarkMapFormFull(b *testing.B) { var s structFull for i := 0; i < b.N; i++ { - mapForm(&s, form) + err := mapForm(&s, form) + if err != nil { + b.Fatalf("Error on a form mapping") + } } b.StopTimer() @@ -52,7 +55,10 @@ type structName struct { func BenchmarkMapFormName(b *testing.B) { var s structName for i := 0; i < b.N; i++ { - mapForm(&s, form) + err := mapForm(&s, form) + if err != nil { + b.Fatalf("Error on a form mapping") + } } b.StopTimer() diff --git a/gin.go b/gin.go index 894cf094..58631263 100644 --- a/gin.go +++ b/gin.go @@ -320,7 +320,10 @@ func (engine *Engine) RunUnix(file string) (err error) { return } defer listener.Close() - os.Chmod(file, 0777) + err = os.Chmod(file, 0777) + if err != nil { + return + } err = http.Serve(listener, engine) return } diff --git a/gin_integration_test.go b/gin_integration_test.go index 7e270b91..d86f610b 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -90,7 +90,8 @@ func TestPusher(t *testing.T) { go func() { router.GET("/pusher", func(c *Context) { if pusher := c.Writer.Pusher(); pusher != nil { - pusher.Push("/assets/app.js", nil) + err := pusher.Push("/assets/app.js", nil) + assert.NoError(t, err) } c.String(http.StatusOK, "it worked") }) @@ -239,6 +240,7 @@ func TestBadListener(t *testing.T) { addr, err := net.ResolveTCPAddr("tcp", "localhost:10086") assert.NoError(t, err) listener, err := net.ListenTCP("tcp", addr) + assert.NoError(t, err) listener.Close() assert.Error(t, router.RunListener(listener)) } diff --git a/render/render_test.go b/render/render_test.go index 95a01b63..376733df 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -347,7 +347,10 @@ func TestRenderRedirect(t *testing.T) { } w = httptest.NewRecorder() - assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() { data2.Render(w) }) + assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() { + err := data2.Render(w) + assert.NoError(t, err) + }) data3 := Redirect{ Code: http.StatusCreated, From 517eacb4f9ca7276511841c63e2911d6ec94c22a Mon Sep 17 00:00:00 2001 From: ishanray Date: Wed, 30 Oct 2019 23:13:39 -0400 Subject: [PATCH 16/90] Update gin.go (#2110) --- gin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gin.go b/gin.go index 58631263..caf4cf2c 100644 --- a/gin.go +++ b/gin.go @@ -30,7 +30,7 @@ type HandlerFunc func(*Context) // HandlersChain defines a HandlerFunc array. type HandlersChain []HandlerFunc -// Last returns the last handler in the chain. ie. the last handler is the main own. +// Last returns the last handler in the chain. ie. the last handler is the main one. func (c HandlersChain) Last() HandlerFunc { if length := len(c); length > 0 { return c[length-1] From aabaccbba2b670e3625c9d9e89b4157a47f052b8 Mon Sep 17 00:00:00 2001 From: Shamus Taylor Date: Thu, 31 Oct 2019 09:52:02 -0500 Subject: [PATCH 17/90] Close files opened in static file handler (#2118) * Close files opened in static file handler * Do not use defer --- routergroup.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routergroup.go b/routergroup.go index a1e6c928..2e7a5b90 100644 --- a/routergroup.go +++ b/routergroup.go @@ -193,13 +193,15 @@ func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileS file := c.Param("filepath") // Check if file exists and/or if we have permission to access it - if _, err := fs.Open(file); err != nil { + f, err := fs.Open(file) + if err != nil { c.Writer.WriteHeader(http.StatusNotFound) c.handlers = group.engine.noRoute // Reset index c.index = -1 return } + f.Close() fileServer.ServeHTTP(c.Writer, c.Request) } From 0f951956d0b8b4b459a2f46bcd4e7118f0306210 Mon Sep 17 00:00:00 2001 From: linfangrong Date: Thu, 31 Oct 2019 23:17:12 +0800 Subject: [PATCH 18/90] [FIX] c.Request.FormFile maybe file, need close (#2114) --- context.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/context.go b/context.go index 18d328d2..046f284e 100644 --- a/context.go +++ b/context.go @@ -516,7 +516,11 @@ func (c *Context) FormFile(name string) (*multipart.FileHeader, error) { return nil, err } } - _, fh, err := c.Request.FormFile(name) + f, fh, err := c.Request.FormFile(name) + if err != nil { + return nil, err + } + f.Close() return fh, err } From db9174ae0c2587fe1c755def0f88cb9aba9e9641 Mon Sep 17 00:00:00 2001 From: Dmitry Kutakov Date: Fri, 1 Nov 2019 03:47:40 +0100 Subject: [PATCH 19/90] fix ignore walking on form mapping (#1942) (#1943) --- binding/form_mapping.go | 7 ++++--- binding/form_mapping_test.go | 10 ++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index ec78bfee..d6199c4f 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -51,6 +51,10 @@ func mappingByPtr(ptr interface{}, setter setter, tag string) error { } func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) { + if field.Tag.Get(tag) == "-" { // just ignoring this field + return false, nil + } + var vKind = value.Kind() if vKind == reflect.Ptr { @@ -112,9 +116,6 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter tagValue = field.Tag.Get(tag) tagValue, opts := head(tagValue, ",") - if tagValue == "-" { // just ignoring this field - return false, nil - } if tagValue == "" { // default value is FieldName tagValue = field.Name } diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index c9d6111b..2a560371 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -269,3 +269,13 @@ func TestMappingMapField(t *testing.T) { assert.NoError(t, err) assert.Equal(t, map[string]int{"one": 1}, s.M) } + +func TestMappingIgnoredCircularRef(t *testing.T) { + type S struct { + S *S `form:"-"` + } + var s S + + err := mappingByPtr(&s, formSource{}, "form") + assert.NoError(t, err) +} From 15ced05c5316609bce5b43389f8e3f06102a8b18 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 24 Nov 2019 10:25:21 +0800 Subject: [PATCH 20/90] ready to release v1.5.0 (#2109) * ready to release v1.5.0 * add some commit log * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * Update CHANGELOG.md Co-Authored-By: Dominik-K * remove refactor and update readme pr --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++-- version.go | 2 +- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ccd2faf..0bb90f22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,39 @@ +### Gin v1.5.0 -### Gin 1.4.0 +- [FIX] Use DefaultWriter and DefaultErrorWriter for debug messages [#1891](https://github.com/gin-gonic/gin/pull/1891) +- [NEW] Now you can parse the inline lowercase start structure [#1893](https://github.com/gin-gonic/gin/pull/1893) +- [FIX] Some code improvements [#1909](https://github.com/gin-gonic/gin/pull/1909) +- [FIX] Use encode replace json marshal increase json encoder speed [#1546](https://github.com/gin-gonic/gin/pull/1546) +- [NEW] Hold matched route full path in the Context [#1826](https://github.com/gin-gonic/gin/pull/1826) +- [FIX] Fix context.Params race condition on Copy() [#1841](https://github.com/gin-gonic/gin/pull/1841) +- [NEW] Add context param query cache [#1450](https://github.com/gin-gonic/gin/pull/1450) +- [FIX] Improve GetQueryMap performance [#1918](https://github.com/gin-gonic/gin/pull/1918) +- [FIX] Improve get post data [#1920](https://github.com/gin-gonic/gin/pull/1920) +- [FIX] Use context instead of x/net/context [#1922](https://github.com/gin-gonic/gin/pull/1922) +- [FIX] Attempt to fix PostForm cache bug [#1931](https://github.com/gin-gonic/gin/pull/1931) +- [NEW] Add support of multipart multi files [#1949](https://github.com/gin-gonic/gin/pull/1949) +- [NEW] Support bind http header param [#1957](https://github.com/gin-gonic/gin/pull/1957) +- [FIX] Drop support for go1.8 and go1.9 [#1933](https://github.com/gin-gonic/gin/pull/1933) +- [FIX] Bugfix for the FullPath feature [#1919](https://github.com/gin-gonic/gin/pull/1919) +- [FIX] Gin1.5 bytes.Buffer to strings.Builder [#1939](https://github.com/gin-gonic/gin/pull/1939) +- [FIX] Upgrade github.com/ugorji/go/codec [#1969](https://github.com/gin-gonic/gin/pull/1969) +- [NEW] Support bind unix time [#1980](https://github.com/gin-gonic/gin/pull/1980) +- [FIX] Simplify code [#2004](https://github.com/gin-gonic/gin/pull/2004) +- [NEW] Support negative Content-Length in DataFromReader [#1981](https://github.com/gin-gonic/gin/pull/1981) +- [FIX] Identify terminal on a RISC-V architecture for auto-colored logs [#2019](https://github.com/gin-gonic/gin/pull/2019) +- [BREAKING] `Context.JSONP()` now expects a semicolon (`;`) at the end [#2007](https://github.com/gin-gonic/gin/pull/2007) +- [BREAKING] Upgrade default `binding.Validator` to v9 (see [its changelog](https://github.com/go-playground/validator/releases/tag/v9.0.0)) [#1015](https://github.com/gin-gonic/gin/pull/1015) +- [NEW] Add `DisallowUnknownFields()` in `Context.BindJSON()` [#2028](https://github.com/gin-gonic/gin/pull/2028) +- [NEW] Use specific `net.Listener` with `Engine.RunListener()` [#2023](https://github.com/gin-gonic/gin/pull/2023) +- [FIX] Fix some typo [#2079](https://github.com/gin-gonic/gin/pull/2079) [#2080](https://github.com/gin-gonic/gin/pull/2080) +- [FIX] Relocate binding body tests [#2086](https://github.com/gin-gonic/gin/pull/2086) +- [FIX] Use Writer in Context.Status [#1606](https://github.com/gin-gonic/gin/pull/1606) +- [FIX] `Engine.RunUnix()` now returns the error if it can't change the file mode [#2093](https://github.com/gin-gonic/gin/pull/2093) +- [FIX] `RouterGroup.StaticFS()` leaked files. Now it closes them. [#2118](https://github.com/gin-gonic/gin/pull/2118) +- [FIX] `Context.Request.FormFile` leaked file. Now it closes it. [#2114](https://github.com/gin-gonic/gin/pull/2114) +- [FIX] Ignore walking on `form:"-"` mapping [#1943](https://github.com/gin-gonic/gin/pull/1943) + +### Gin v1.4.0 - [NEW] Support for [Go Modules](https://github.com/golang/go/wiki/Modules) [#1569](https://github.com/gin-gonic/gin/pull/1569) - [NEW] Refactor of form mapping multipart request [#1829](https://github.com/gin-gonic/gin/pull/1829) @@ -56,7 +90,7 @@ - [NEW] Upgrade dependency libraries [#1491](https://github.com/gin-gonic/gin/pull/1491) -### Gin 1.3.0 +### Gin v1.3.0 - [NEW] Add [`func (*Context) QueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.QueryMap), [`func (*Context) GetQueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetQueryMap), [`func (*Context) PostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.PostFormMap) and [`func (*Context) GetPostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetPostFormMap) to support `type map[string]string` as query string or form parameters, see [#1383](https://github.com/gin-gonic/gin/pull/1383) - [NEW] Add [`func (*Context) AsciiJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.AsciiJSON), see [#1358](https://github.com/gin-gonic/gin/pull/1358) diff --git a/version.go b/version.go index 028caebe..6f8235f9 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.4.0-dev" +const Version = "v1.5.0" From 70ca31bc113523fa1e1309c01d5b3249ddfdab23 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Sun, 24 Nov 2019 16:22:18 +0800 Subject: [PATCH 21/90] fix comment in `mode.go` (#2129) EnableJsonDisallowUnknownFields => EnableJsonDecoderDisallowUnknownFields --- mode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode.go b/mode.go index c3c37fdc..edfc2940 100644 --- a/mode.go +++ b/mode.go @@ -77,7 +77,7 @@ func EnableJsonDecoderUseNumber() { binding.EnableDecoderUseNumber = true } -// EnableJsonDisallowUnknownFields sets true for binding.EnableDecoderDisallowUnknownFields to +// EnableJsonDecoderDisallowUnknownFields sets true for binding.EnableDecoderDisallowUnknownFields to // call the DisallowUnknownFields method on the JSON Decoder instance. func EnableJsonDecoderDisallowUnknownFields() { binding.EnableDecoderDisallowUnknownFields = true From 2ee0e963942d91be7944dfcc07dc8c02a4a78566 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 24 Nov 2019 23:07:56 +0800 Subject: [PATCH 22/90] Drop support go1.10 (#2147) --- .travis.yml | 1 - README.md | 2 +- debug.go | 2 +- debug_test.go | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 748a07a7..4a4ab817 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: go matrix: fast_finish: true include: - - go: 1.10.x - go: 1.11.x env: GO111MODULE=on - go: 1.12.x diff --git a/README.md b/README.md index 3dd99d99..8aa50509 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi To install Gin package, you need to install Go and set your Go workspace first. -1. The first need [Go](https://golang.org/) installed (**version 1.10+ is required**), then you can use the below Go command to install Gin. +1. The first need [Go](https://golang.org/) installed (**version 1.11+ is required**), then you can use the below Go command to install Gin. ```sh $ go get -u github.com/gin-gonic/gin diff --git a/debug.go b/debug.go index 49080dbf..c66ca440 100644 --- a/debug.go +++ b/debug.go @@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) { func debugPrintWARNINGDefault() { if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer { - debugPrint(`[WARNING] Now Gin requires Go 1.10 or later and Go 1.11 will be required soon. + debugPrint(`[WARNING] Now Gin requires Go 1.11 or later and Go 1.12 will be required soon. `) } diff --git a/debug_test.go b/debug_test.go index d6f320ef..d707b4bf 100644 --- a/debug_test.go +++ b/debug_test.go @@ -91,7 +91,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) { }) m, e := getMinVer(runtime.Version()) if e == nil && m <= ginSupportMinGoVer { - assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.10 or later and Go 1.11 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) + assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.11 or later and Go 1.12 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } else { assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re) } From b52a1a1588f8af4e6d1e2a711adbae42d11bb59d Mon Sep 17 00:00:00 2001 From: Dmitry Kutakov Date: Mon, 25 Nov 2019 03:45:53 +0100 Subject: [PATCH 23/90] allow empty headers on DataFromReader (#2121) --- context_test.go | 17 +++++++++++++++++ render/reader.go | 3 +++ render/reader_test.go | 23 +++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 render/reader_test.go diff --git a/context_test.go b/context_test.go index f7bb0f51..18709d3d 100644 --- a/context_test.go +++ b/context_test.go @@ -1799,6 +1799,23 @@ func TestContextRenderDataFromReader(t *testing.T) { assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition")) } +func TestContextRenderDataFromReaderNoHeaders(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + body := "#!PNG some raw data" + reader := strings.NewReader(body) + contentLength := int64(len(body)) + contentType := "image/png" + + c.DataFromReader(http.StatusOK, contentLength, contentType, reader, nil) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, body, w.Body.String()) + assert.Equal(t, contentType, w.Header().Get("Content-Type")) + assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length")) +} + type TestResponseRecorder struct { *httptest.ResponseRecorder closeChannel chan bool diff --git a/render/reader.go b/render/reader.go index 502d9398..d5282e49 100644 --- a/render/reader.go +++ b/render/reader.go @@ -22,6 +22,9 @@ type Reader struct { func (r Reader) Render(w http.ResponseWriter) (err error) { r.WriteContentType(w) if r.ContentLength >= 0 { + if r.Headers == nil { + r.Headers = map[string]string{} + } r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10) } r.writeHeaders(w, r.Headers) diff --git a/render/reader_test.go b/render/reader_test.go new file mode 100644 index 00000000..3930f51d --- /dev/null +++ b/render/reader_test.go @@ -0,0 +1,23 @@ +// Copyright 2019 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "net/http/httptest" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestReaderRenderNoHeaders(t *testing.T) { + content := "test" + r := Reader{ + ContentLength: int64(len(content)), + Reader: strings.NewReader(content), + } + err := r.Render(httptest.NewRecorder()) + require.NoError(t, err) +} From 3737520f17457b8a06a35f612607cb4799e53a67 Mon Sep 17 00:00:00 2001 From: BradyBromley <51128276+BradyBromley@users.noreply.github.com> Date: Sun, 24 Nov 2019 19:03:36 -0800 Subject: [PATCH 24/90] Changed wording for clarity in README.md (#2122) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8aa50509..3f2d3c2c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![Open Source Helpers](https://www.codetriage.com/gin-gonic/gin/badges/users.svg)](https://www.codetriage.com/gin-gonic/gin) [![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases) -Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. +Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. ## Contents From e90e2ba9b369057e4ee419a06486bae83313cf54 Mon Sep 17 00:00:00 2001 From: Xudong Cai Date: Mon, 25 Nov 2019 14:49:45 +0800 Subject: [PATCH 25/90] upgrade go-validator to v10 (#2149) * upgrade go-validator to v10 * fix fmt --- binding/default_validator.go | 2 +- binding/validate_test.go | 2 +- go.mod | 6 +----- go.sum | 22 ++++++++++++---------- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/binding/default_validator.go b/binding/default_validator.go index 50e0d57c..a4c1a7f6 100644 --- a/binding/default_validator.go +++ b/binding/default_validator.go @@ -8,7 +8,7 @@ import ( "reflect" "sync" - "gopkg.in/go-playground/validator.v9" + "github.com/go-playground/validator/v10" ) type defaultValidator struct { diff --git a/binding/validate_test.go b/binding/validate_test.go index 81f78834..5299fbf6 100644 --- a/binding/validate_test.go +++ b/binding/validate_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" + "github.com/go-playground/validator/v10" "github.com/stretchr/testify/assert" - "gopkg.in/go-playground/validator.v9" ) type testInterface interface { diff --git a/go.mod b/go.mod index 34151852..1213bd23 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,11 @@ go 1.12 require ( github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/locales v0.12.1 // indirect - github.com/go-playground/universal-translator v0.16.0 // indirect + github.com/go-playground/validator/v10 v10.0.1 github.com/golang/protobuf v1.3.2 github.com/json-iterator/go v1.1.7 - github.com/leodido/go-urn v1.1.0 // indirect github.com/mattn/go-isatty v0.0.9 github.com/stretchr/testify v1.4.0 github.com/ugorji/go/codec v1.1.7 - gopkg.in/go-playground/assert.v1 v1.2.1 // indirect - gopkg.in/go-playground/validator.v9 v9.29.1 gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index 129ad387..9815f2f4 100644 --- a/go.sum +++ b/go.sum @@ -3,17 +3,21 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= -github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= -github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= -github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.0.1 h1:QgDDZpXlR/L3atIL2PbFt0TpazbtN7N6PxTGcgcyEUg= +github.com/go-playground/validator/v10 v10.0.1/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= -github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= @@ -32,11 +36,9 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= -gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 3c8e29b53c6b8334cc270d8e478c26aa4a1ce4b7 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 25 Nov 2019 15:42:23 +0800 Subject: [PATCH 26/90] drop support govendor (#2148) --- .travis.yml | 2 +- Makefile | 16 +---- README.md | 38 ----------- vendor/vendor.json | 153 --------------------------------------------- 4 files changed, 4 insertions(+), 205 deletions(-) delete mode 100644 vendor/vendor.json diff --git a/.travis.yml b/.travis.yml index 4a4ab817..b80b2577 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ before_install: - if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi install: - - if [[ "${GO111MODULE}" = "on" ]]; then go mod download; else make install; fi + - if [[ "${GO111MODULE}" = "on" ]]; then go mod download; fi - if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi - if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi diff --git a/Makefile b/Makefile index 51a6b916..e69dbd8b 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,10 @@ GO ?= go GOFMT ?= gofmt "-s" -PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/) -VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/ | grep -v /examples/) -GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*") +PACKAGES ?= $(shell $(GO) list ./...) +VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /examples/) +GOFILES := $(shell find . -name "*.go") TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | grep -v examples) -all: install - -install: deps - govendor sync - .PHONY: test test: echo "mode: count" > coverage.out @@ -48,11 +43,6 @@ fmt-check: vet: $(GO) vet $(VETPACKAGES) -deps: - @hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - $(GO) get -u github.com/kardianos/govendor; \ - fi - .PHONY: lint lint: @hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ diff --git a/README.md b/README.md index 3f2d3c2c..012152a0 100644 --- a/README.md +++ b/README.md @@ -88,44 +88,6 @@ 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 $GOPATH/src/github.com/myusername/project && cd "$_" -``` - -If you are on a Mac and you're installing Go 1.8 (released: Feb 2017) or later, GOPATH is automatically determined by the Go toolchain for you. It defaults to $HOME/go on macOS so you can create your project like this - -```sh -$ mkdir -p $HOME/go/src/github.com/myusername/project && cd "$_" -``` - -3. Vendor init your project and add gin - -```sh -$ govendor init -$ govendor fetch github.com/gin-gonic/gin@v1.3 -``` - -4. Copy a starting template inside your project - -```sh -$ curl https://raw.githubusercontent.com/gin-gonic/examples/master/basic/main.go > main.go -``` - -5. Run your project - -```sh -$ go run main.go -``` - ## Quick start ```sh diff --git a/vendor/vendor.json b/vendor/vendor.json deleted file mode 100644 index 70b2d9eb..00000000 --- a/vendor/vendor.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "comment": "v1.4.0", - "ignore": "test", - "package": [ - { - "checksumSHA1": "CSPbwbyzqA6sfORicn4HFtIhF/c=", - "path": "github.com/davecgh/go-spew/spew", - "revision": "8991bc29aa16c548c550c7ff78260e27b9ab7c73", - "revisionTime": "2018-02-21T22:46:20Z", - "version": "v1.1", - "versionExact": "v1.1.1" - }, - { - "checksumSHA1": "qlEzrgKgIkh7y0ePm9BNo1cNdXo=", - "path": "github.com/gin-contrib/sse", - "revision": "54d8467d122d380a14768b6b4e5cd7ca4755938f", - "revisionTime": "2019-06-02T15:02:53Z", - "version": "v0.1", - "versionExact": "v0.1.0" - }, - { - "checksumSHA1": "b4DmyMT9bicTRVJw1hJXHLhIH+0=", - "path": "github.com/go-playground/locales", - "revision": "f63010822830b6fe52288ee52d5a1151088ce039", - "revisionTime": "2018-03-23T16:04:04Z", - "version": "v0.12", - "versionExact": "v0.12.1" - }, - { - "checksumSHA1": "JgF260rC9YpWyY5WEljjimWLUXs=", - "path": "github.com/go-playground/locales/currency", - "revision": "630ebbb602847eba93e75ae38bbc7bb7abcf1ff3", - "revisionTime": "2019-04-30T15:33:29Z" - }, - { - "checksumSHA1": "9pKcUHBaVS+360X6h4IowhmOPjk=", - "path": "github.com/go-playground/universal-translator", - "revision": "b32fa301c9fe55953584134cb6853a13c87ec0a1", - "revisionTime": "2017-02-09T16:11:52Z", - "version": "v0.16", - "versionExact": "v0.16.0" - }, - { - "checksumSHA1": "Y2MOwzNZfl4NRNDbLCZa6sgx7O0=", - "path": "github.com/golang/protobuf/proto", - "revision": "c823c79ea1570fb5ff454033735a8e68575d1d0f", - "revisionTime": "2019-02-05T22:20:52Z", - "version": "v1.3", - "versionExact": "v1.3.0" - }, - { - "checksumSHA1": "zNo6yGy/bCJuzkEcP70oEBtOB2M=", - "path": "github.com/leodido/go-urn", - "revision": "70078a794e8ea4b497ba7c19a78cd60f90ccf0f4", - "revisionTime": "2018-05-24T03:26:21Z", - "version": "v1.1", - "versionExact": "v1.1.0" - }, - { - "checksumSHA1": "TB2vxux9xQbvsTHOVt4aRTuvSn4=", - "path": "github.com/json-iterator/go", - "revision": "0ff49de124c6f76f8494e194af75bde0f1a49a29", - "revisionTime": "2019-03-06T14:29:09Z", - "version": "v1.1", - "versionExact": "v1.1.6" - }, - { - "checksumSHA1": "Ya+baVBU/RkXXUWD3LGFmGJiiIg=", - "path": "github.com/mattn/go-isatty", - "revision": "c2a7a6ca930a4cd0bc33a3f298eb71960732a3a7", - "revisionTime": "2019-03-12T13:58:54Z", - "version": "v0.0", - "versionExact": "v0.0.7" - }, - { - "checksumSHA1": "ZTcgWKWHsrX0RXYVXn5Xeb8Q0go=", - "path": "github.com/modern-go/concurrent", - "revision": "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94", - "revisionTime": "2018-03-06T01:26:44Z" - }, - { - "checksumSHA1": "qvH48wzTIV3QKSDqI0dLFtVjaDI=", - "path": "github.com/modern-go/reflect2", - "revision": "94122c33edd36123c84d5368cfb2b69df93a0ec8", - "revisionTime": "2018-07-18T01:23:57Z" - }, - { - "checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=", - "path": "github.com/pmezard/go-difflib/difflib", - "revision": "5d4384ee4fb2527b0a1256a821ebfc92f91efefc", - "revisionTime": "2018-12-26T10:54:42Z" - }, - { - "checksumSHA1": "cpNsoLqBprpKh+VZTBOZNVXzBEk=", - "path": "github.com/stretchr/objx", - "revision": "c61a9dfcced1815e7d40e214d00d1a8669a9f58c", - "revisionTime": "2019-02-11T16:23:28Z" - }, - { - "checksumSHA1": "DBdcVxnvaINHhWyyGgih/Mel6gE=", - "path": "github.com/stretchr/testify", - "revision": "ffdc059bfe9ce6a4e144ba849dbedead332c6053", - "revisionTime": "2018-12-05T02:12:43Z", - "version": "v1.3", - "versionExact": "v1.3.0" - }, - { - "checksumSHA1": "c6pbpF7eowwO59phRTpF8cQ80Z0=", - "path": "github.com/stretchr/testify/assert", - "revision": "f35b8ab0b5a2cef36673838d662e249dd9c94686", - "revisionTime": "2018-05-06T18:05:49Z", - "version": "v1.2", - "versionExact": "v1.2.2" - }, - { - "checksumSHA1": "wnEANt4k5X/KGwoFyfSSnpxULm4=", - "path": "github.com/stretchr/testify/require", - "revision": "f35b8ab0b5a2cef36673838d662e249dd9c94686", - "revisionTime": "2018-05-06T18:05:49Z" - }, - { - "checksumSHA1": "S4ei9eSqVThDio0Jn2sav6yUbvg=", - "path": "github.com/ugorji/go/codec", - "revision": "82dbfaf494e3b01d2d481376f11f6a5c8cf9599f", - "revisionTime": "2019-07-02T14:15:27Z", - "version": "v1.1", - "versionExact": "v1.1.6" - }, - { - "checksumSHA1": "2gaep1KNRDNyDA3O+KgPTQsGWvs=", - "path": "golang.org/x/sys/unix", - "revision": "a43fa875dd822b81eb6d2ad538bc1f4caba169bd", - "revisionTime": "2019-05-02T15:41:39Z" - }, - { - "checksumSHA1": "ACzc7AkwLtNgKhqtj8V7SGUJgnw=", - "path": "gopkg.in/go-playground/validator.v9", - "revision": "46b4b1e301c24cac870ffcb4ba5c8a703d1ef475", - "revisionTime": "2019-03-31T13:31:25Z", - "version": "v9.28", - "versionExact": "v9.28.0" - }, - { - "checksumSHA1": "QqDq2x8XOU7IoOR98Cx1eiV5QY8=", - "path": "gopkg.in/yaml.v2", - "revision": "51d6538a90f86fe93ac480b35f37b2be17fef232", - "revisionTime": "2018-11-15T11:05:04Z", - "version": "v2.2", - "versionExact": "v2.2.2" - } - ], - "rootPath": "github.com/gin-gonic/gin" -} From 231ff00d1f77d30f8dd97278680d7731e1317d55 Mon Sep 17 00:00:00 2001 From: Ngalim Siregar Date: Tue, 26 Nov 2019 07:19:30 +0700 Subject: [PATCH 27/90] Refactor redirect request in gin.go (#1970) * Refactor redirect request in gin.go * Update http status code --- binding/form.go | 2 +- gin.go | 31 ++++++++++++++++--------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/binding/form.go b/binding/form.go index 9e9fc3de..b93c34cf 100644 --- a/binding/form.go +++ b/binding/form.go @@ -8,7 +8,7 @@ import ( "net/http" ) -const defaultMemory = 32 * 1024 * 1024 +const defaultMemory = 32 << 20 type formBinding struct{} type formPostBinding struct{} diff --git a/gin.go b/gin.go index caf4cf2c..c194d6bf 100644 --- a/gin.go +++ b/gin.go @@ -457,18 +457,11 @@ func redirectTrailingSlash(c *Context) { if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." { p = prefix + "/" + req.URL.Path } - code := http.StatusMovedPermanently // Permanent redirect, request with GET method - if req.Method != "GET" { - code = http.StatusTemporaryRedirect - } - req.URL.Path = p + "/" if length := len(p); length > 1 && p[length-1] == '/' { req.URL.Path = p[:length-1] } - debugPrint("redirecting request %d: %s --> %s", code, p, req.URL.String()) - http.Redirect(c.Writer, req, req.URL.String(), code) - c.writermem.WriteHeaderNow() + redirectRequest(c) } func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool { @@ -476,15 +469,23 @@ func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool { rPath := req.URL.Path if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(rPath), trailingSlash); ok { - code := http.StatusMovedPermanently // Permanent redirect, request with GET method - if req.Method != "GET" { - code = http.StatusTemporaryRedirect - } req.URL.Path = string(fixedPath) - debugPrint("redirecting request %d: %s --> %s", code, rPath, req.URL.String()) - http.Redirect(c.Writer, req, req.URL.String(), code) - c.writermem.WriteHeaderNow() + redirectRequest(c) return true } return false } + +func redirectRequest(c *Context) { + req := c.Request + rPath := req.URL.Path + rURL := req.URL.String() + + code := http.StatusMovedPermanently // Permanent redirect, request with GET method + if req.Method != "GET" { + code = http.StatusTemporaryRedirect + } + debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL) + http.Redirect(c.Writer, req, rURL, code) + c.writermem.WriteHeaderNow() +} From 352d69c71f45d51971f2d23f5c4450c6410aa481 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 29 Nov 2019 00:02:02 +0800 Subject: [PATCH 28/90] =?UTF-8?q?chore(performance):=20Improve=20performan?= =?UTF-8?q?ce=20for=20adding=20RemoveExtraS=E2=80=A6=20(#2159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: Add RemoveExtraSlash flag * fix testing Signed-off-by: Bo-Yi Wu --- gin.go | 10 +++++++++- routes_test.go | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/gin.go b/gin.go index c194d6bf..f0cd09e4 100644 --- a/gin.go +++ b/gin.go @@ -97,6 +97,10 @@ type Engine struct { // method call. MaxMultipartMemory int64 + // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes. + // See the PR #1817 and issue #1644 + RemoveExtraSlash bool + delims render.Delims secureJsonPrefix string HTMLRender render.HTMLRender @@ -134,6 +138,7 @@ func New() *Engine { ForwardedByClientIP: true, AppEngine: defaultAppEngine, UseRawPath: false, + RemoveExtraSlash: false, UnescapePathValues: true, MaxMultipartMemory: defaultMultipartMemory, trees: make(methodTrees, 0, 9), @@ -385,7 +390,10 @@ func (engine *Engine) handleHTTPRequest(c *Context) { rPath = c.Request.URL.RawPath unescape = engine.UnescapePathValues } - rPath = cleanPath(rPath) + + if engine.RemoveExtraSlash { + rPath = cleanPath(rPath) + } // Find root of the tree for the given HTTP method t := engine.trees diff --git a/routes_test.go b/routes_test.go index 0c2f9a0c..d1ddbe91 100644 --- a/routes_test.go +++ b/routes_test.go @@ -263,6 +263,7 @@ func TestRouteParamsByNameWithExtraSlash(t *testing.T) { lastName := "" wild := "" router := New() + router.RemoveExtraSlash = true router.GET("/test/:name/:last_name/*wild", func(c *Context) { name = c.Params.ByName("name") lastName = c.Params.ByName("last_name") @@ -407,6 +408,29 @@ func TestRouteNotAllowedDisabled(t *testing.T) { assert.Equal(t, http.StatusNotFound, w.Code) } +func TestRouterNotFoundWithRemoveExtraSlash(t *testing.T) { + router := New() + router.RemoveExtraSlash = true + router.GET("/path", func(c *Context) {}) + router.GET("/", func(c *Context) {}) + + testRoutes := []struct { + route string + code int + location string + }{ + {"/../path", http.StatusOK, ""}, // CleanPath + {"/nope", http.StatusNotFound, ""}, // NotFound + } + for _, tr := range testRoutes { + w := performRequest(router, "GET", tr.route) + assert.Equal(t, tr.code, w.Code) + if w.Code != http.StatusNotFound { + assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) + } + } +} + func TestRouterNotFound(t *testing.T) { router := New() router.RedirectFixedPath = true @@ -419,14 +443,14 @@ func TestRouterNotFound(t *testing.T) { code int location string }{ - {"/path/", http.StatusMovedPermanently, "/path"}, // TSR -/ - {"/dir", http.StatusMovedPermanently, "/dir/"}, // TSR +/ - {"/PATH", http.StatusMovedPermanently, "/path"}, // Fixed Case - {"/DIR/", http.StatusMovedPermanently, "/dir/"}, // Fixed Case - {"/PATH/", http.StatusMovedPermanently, "/path"}, // Fixed Case -/ - {"/DIR", http.StatusMovedPermanently, "/dir/"}, // Fixed Case +/ - {"/../path", http.StatusOK, ""}, // CleanPath - {"/nope", http.StatusNotFound, ""}, // NotFound + {"/path/", http.StatusMovedPermanently, "/path"}, // TSR -/ + {"/dir", http.StatusMovedPermanently, "/dir/"}, // TSR +/ + {"/PATH", http.StatusMovedPermanently, "/path"}, // Fixed Case + {"/DIR/", http.StatusMovedPermanently, "/dir/"}, // Fixed Case + {"/PATH/", http.StatusMovedPermanently, "/path"}, // Fixed Case -/ + {"/DIR", http.StatusMovedPermanently, "/dir/"}, // Fixed Case +/ + {"/../path", http.StatusMovedPermanently, "/path"}, // Without CleanPath + {"/nope", http.StatusNotFound, ""}, // NotFound } for _, tr := range testRoutes { w := performRequest(router, "GET", tr.route) From d5f12ac6d7ba14b9bc85fa7d0ae486289d52bdf1 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Fri, 29 Nov 2019 07:50:49 +0800 Subject: [PATCH 29/90] use http method constant (#2155) * use http method constant * fix typo --- binding/binding.go | 2 +- gin.go | 2 +- gin_integration_test.go | 2 +- githubapi_test.go | 498 ++++++++++++++++++++-------------------- logger.go | 14 +- routergroup.go | 32 +-- routergroup_test.go | 32 +-- routes_test.go | 156 ++++++------- 8 files changed, 369 insertions(+), 369 deletions(-) diff --git a/binding/binding.go b/binding/binding.go index 6d58c3cd..f578aa55 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -84,7 +84,7 @@ var ( // Default returns the appropriate Binding instance based on the HTTP method // and the content type. func Default(method, contentType string) Binding { - if method == "GET" { + if method == http.MethodGet { return Form } diff --git a/gin.go b/gin.go index f0cd09e4..71f3fd5c 100644 --- a/gin.go +++ b/gin.go @@ -490,7 +490,7 @@ func redirectRequest(c *Context) { rURL := req.URL.String() code := http.StatusMovedPermanently // Permanent redirect, request with GET method - if req.Method != "GET" { + if req.Method != http.MethodGet { code = http.StatusTemporaryRedirect } debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL) diff --git a/gin_integration_test.go b/gin_integration_test.go index d86f610b..f29d1fc1 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -291,7 +291,7 @@ func TestConcurrentHandleContext(t *testing.T) { // } func testGetRequestHandler(t *testing.T, h http.Handler, url string) { - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) assert.NoError(t, err) w := httptest.NewRecorder() diff --git a/githubapi_test.go b/githubapi_test.go index fb74d659..925c5a14 100644 --- a/githubapi_test.go +++ b/githubapi_test.go @@ -24,265 +24,265 @@ type route struct { // http://developer.github.com/v3/ var githubAPI = []route{ // OAuth Authorizations - {"GET", "/authorizations"}, - {"GET", "/authorizations/:id"}, - {"POST", "/authorizations"}, - //{"PUT", "/authorizations/clients/:client_id"}, - //{"PATCH", "/authorizations/:id"}, - {"DELETE", "/authorizations/:id"}, - {"GET", "/applications/:client_id/tokens/:access_token"}, - {"DELETE", "/applications/:client_id/tokens"}, - {"DELETE", "/applications/:client_id/tokens/:access_token"}, + {http.MethodGet, "/authorizations"}, + {http.MethodGet, "/authorizations/:id"}, + {http.MethodPost, "/authorizations"}, + //{http.MethodPut, "/authorizations/clients/:client_id"}, + //{http.MethodPatch, "/authorizations/:id"}, + {http.MethodDelete, "/authorizations/:id"}, + {http.MethodGet, "/applications/:client_id/tokens/:access_token"}, + {http.MethodDelete, "/applications/:client_id/tokens"}, + {http.MethodDelete, "/applications/:client_id/tokens/:access_token"}, // Activity - {"GET", "/events"}, - {"GET", "/repos/:owner/:repo/events"}, - {"GET", "/networks/:owner/:repo/events"}, - {"GET", "/orgs/:org/events"}, - {"GET", "/users/:user/received_events"}, - {"GET", "/users/:user/received_events/public"}, - {"GET", "/users/:user/events"}, - {"GET", "/users/:user/events/public"}, - {"GET", "/users/:user/events/orgs/:org"}, - {"GET", "/feeds"}, - {"GET", "/notifications"}, - {"GET", "/repos/:owner/:repo/notifications"}, - {"PUT", "/notifications"}, - {"PUT", "/repos/:owner/:repo/notifications"}, - {"GET", "/notifications/threads/:id"}, - //{"PATCH", "/notifications/threads/:id"}, - {"GET", "/notifications/threads/:id/subscription"}, - {"PUT", "/notifications/threads/:id/subscription"}, - {"DELETE", "/notifications/threads/:id/subscription"}, - {"GET", "/repos/:owner/:repo/stargazers"}, - {"GET", "/users/:user/starred"}, - {"GET", "/user/starred"}, - {"GET", "/user/starred/:owner/:repo"}, - {"PUT", "/user/starred/:owner/:repo"}, - {"DELETE", "/user/starred/:owner/:repo"}, - {"GET", "/repos/:owner/:repo/subscribers"}, - {"GET", "/users/:user/subscriptions"}, - {"GET", "/user/subscriptions"}, - {"GET", "/repos/:owner/:repo/subscription"}, - {"PUT", "/repos/:owner/:repo/subscription"}, - {"DELETE", "/repos/:owner/:repo/subscription"}, - {"GET", "/user/subscriptions/:owner/:repo"}, - {"PUT", "/user/subscriptions/:owner/:repo"}, - {"DELETE", "/user/subscriptions/:owner/:repo"}, + {http.MethodGet, "/events"}, + {http.MethodGet, "/repos/:owner/:repo/events"}, + {http.MethodGet, "/networks/:owner/:repo/events"}, + {http.MethodGet, "/orgs/:org/events"}, + {http.MethodGet, "/users/:user/received_events"}, + {http.MethodGet, "/users/:user/received_events/public"}, + {http.MethodGet, "/users/:user/events"}, + {http.MethodGet, "/users/:user/events/public"}, + {http.MethodGet, "/users/:user/events/orgs/:org"}, + {http.MethodGet, "/feeds"}, + {http.MethodGet, "/notifications"}, + {http.MethodGet, "/repos/:owner/:repo/notifications"}, + {http.MethodPut, "/notifications"}, + {http.MethodPut, "/repos/:owner/:repo/notifications"}, + {http.MethodGet, "/notifications/threads/:id"}, + //{http.MethodPatch, "/notifications/threads/:id"}, + {http.MethodGet, "/notifications/threads/:id/subscription"}, + {http.MethodPut, "/notifications/threads/:id/subscription"}, + {http.MethodDelete, "/notifications/threads/:id/subscription"}, + {http.MethodGet, "/repos/:owner/:repo/stargazers"}, + {http.MethodGet, "/users/:user/starred"}, + {http.MethodGet, "/user/starred"}, + {http.MethodGet, "/user/starred/:owner/:repo"}, + {http.MethodPut, "/user/starred/:owner/:repo"}, + {http.MethodDelete, "/user/starred/:owner/:repo"}, + {http.MethodGet, "/repos/:owner/:repo/subscribers"}, + {http.MethodGet, "/users/:user/subscriptions"}, + {http.MethodGet, "/user/subscriptions"}, + {http.MethodGet, "/repos/:owner/:repo/subscription"}, + {http.MethodPut, "/repos/:owner/:repo/subscription"}, + {http.MethodDelete, "/repos/:owner/:repo/subscription"}, + {http.MethodGet, "/user/subscriptions/:owner/:repo"}, + {http.MethodPut, "/user/subscriptions/:owner/:repo"}, + {http.MethodDelete, "/user/subscriptions/:owner/:repo"}, // Gists - {"GET", "/users/:user/gists"}, - {"GET", "/gists"}, - //{"GET", "/gists/public"}, - //{"GET", "/gists/starred"}, - {"GET", "/gists/:id"}, - {"POST", "/gists"}, - //{"PATCH", "/gists/:id"}, - {"PUT", "/gists/:id/star"}, - {"DELETE", "/gists/:id/star"}, - {"GET", "/gists/:id/star"}, - {"POST", "/gists/:id/forks"}, - {"DELETE", "/gists/:id"}, + {http.MethodGet, "/users/:user/gists"}, + {http.MethodGet, "/gists"}, + //{http.MethodGet, "/gists/public"}, + //{http.MethodGet, "/gists/starred"}, + {http.MethodGet, "/gists/:id"}, + {http.MethodPost, "/gists"}, + //{http.MethodPatch, "/gists/:id"}, + {http.MethodPut, "/gists/:id/star"}, + {http.MethodDelete, "/gists/:id/star"}, + {http.MethodGet, "/gists/:id/star"}, + {http.MethodPost, "/gists/:id/forks"}, + {http.MethodDelete, "/gists/:id"}, // Git Data - {"GET", "/repos/:owner/:repo/git/blobs/:sha"}, - {"POST", "/repos/:owner/:repo/git/blobs"}, - {"GET", "/repos/:owner/:repo/git/commits/:sha"}, - {"POST", "/repos/:owner/:repo/git/commits"}, - //{"GET", "/repos/:owner/:repo/git/refs/*ref"}, - {"GET", "/repos/:owner/:repo/git/refs"}, - {"POST", "/repos/:owner/:repo/git/refs"}, - //{"PATCH", "/repos/:owner/:repo/git/refs/*ref"}, - //{"DELETE", "/repos/:owner/:repo/git/refs/*ref"}, - {"GET", "/repos/:owner/:repo/git/tags/:sha"}, - {"POST", "/repos/:owner/:repo/git/tags"}, - {"GET", "/repos/:owner/:repo/git/trees/:sha"}, - {"POST", "/repos/:owner/:repo/git/trees"}, + {http.MethodGet, "/repos/:owner/:repo/git/blobs/:sha"}, + {http.MethodPost, "/repos/:owner/:repo/git/blobs"}, + {http.MethodGet, "/repos/:owner/:repo/git/commits/:sha"}, + {http.MethodPost, "/repos/:owner/:repo/git/commits"}, + //{http.MethodGet, "/repos/:owner/:repo/git/refs/*ref"}, + {http.MethodGet, "/repos/:owner/:repo/git/refs"}, + {http.MethodPost, "/repos/:owner/:repo/git/refs"}, + //{http.MethodPatch, "/repos/:owner/:repo/git/refs/*ref"}, + //{http.MethodDelete, "/repos/:owner/:repo/git/refs/*ref"}, + {http.MethodGet, "/repos/:owner/:repo/git/tags/:sha"}, + {http.MethodPost, "/repos/:owner/:repo/git/tags"}, + {http.MethodGet, "/repos/:owner/:repo/git/trees/:sha"}, + {http.MethodPost, "/repos/:owner/:repo/git/trees"}, // Issues - {"GET", "/issues"}, - {"GET", "/user/issues"}, - {"GET", "/orgs/:org/issues"}, - {"GET", "/repos/:owner/:repo/issues"}, - {"GET", "/repos/:owner/:repo/issues/:number"}, - {"POST", "/repos/:owner/:repo/issues"}, - //{"PATCH", "/repos/:owner/:repo/issues/:number"}, - {"GET", "/repos/:owner/:repo/assignees"}, - {"GET", "/repos/:owner/:repo/assignees/:assignee"}, - {"GET", "/repos/:owner/:repo/issues/:number/comments"}, - //{"GET", "/repos/:owner/:repo/issues/comments"}, - //{"GET", "/repos/:owner/:repo/issues/comments/:id"}, - {"POST", "/repos/:owner/:repo/issues/:number/comments"}, - //{"PATCH", "/repos/:owner/:repo/issues/comments/:id"}, - //{"DELETE", "/repos/:owner/:repo/issues/comments/:id"}, - {"GET", "/repos/:owner/:repo/issues/:number/events"}, - //{"GET", "/repos/:owner/:repo/issues/events"}, - //{"GET", "/repos/:owner/:repo/issues/events/:id"}, - {"GET", "/repos/:owner/:repo/labels"}, - {"GET", "/repos/:owner/:repo/labels/:name"}, - {"POST", "/repos/:owner/:repo/labels"}, - //{"PATCH", "/repos/:owner/:repo/labels/:name"}, - {"DELETE", "/repos/:owner/:repo/labels/:name"}, - {"GET", "/repos/:owner/:repo/issues/:number/labels"}, - {"POST", "/repos/:owner/:repo/issues/:number/labels"}, - {"DELETE", "/repos/:owner/:repo/issues/:number/labels/:name"}, - {"PUT", "/repos/:owner/:repo/issues/:number/labels"}, - {"DELETE", "/repos/:owner/:repo/issues/:number/labels"}, - {"GET", "/repos/:owner/:repo/milestones/:number/labels"}, - {"GET", "/repos/:owner/:repo/milestones"}, - {"GET", "/repos/:owner/:repo/milestones/:number"}, - {"POST", "/repos/:owner/:repo/milestones"}, - //{"PATCH", "/repos/:owner/:repo/milestones/:number"}, - {"DELETE", "/repos/:owner/:repo/milestones/:number"}, + {http.MethodGet, "/issues"}, + {http.MethodGet, "/user/issues"}, + {http.MethodGet, "/orgs/:org/issues"}, + {http.MethodGet, "/repos/:owner/:repo/issues"}, + {http.MethodGet, "/repos/:owner/:repo/issues/:number"}, + {http.MethodPost, "/repos/:owner/:repo/issues"}, + //{http.MethodPatch, "/repos/:owner/:repo/issues/:number"}, + {http.MethodGet, "/repos/:owner/:repo/assignees"}, + {http.MethodGet, "/repos/:owner/:repo/assignees/:assignee"}, + {http.MethodGet, "/repos/:owner/:repo/issues/:number/comments"}, + //{http.MethodGet, "/repos/:owner/:repo/issues/comments"}, + //{http.MethodGet, "/repos/:owner/:repo/issues/comments/:id"}, + {http.MethodPost, "/repos/:owner/:repo/issues/:number/comments"}, + //{http.MethodPatch, "/repos/:owner/:repo/issues/comments/:id"}, + //{http.MethodDelete, "/repos/:owner/:repo/issues/comments/:id"}, + {http.MethodGet, "/repos/:owner/:repo/issues/:number/events"}, + //{http.MethodGet, "/repos/:owner/:repo/issues/events"}, + //{http.MethodGet, "/repos/:owner/:repo/issues/events/:id"}, + {http.MethodGet, "/repos/:owner/:repo/labels"}, + {http.MethodGet, "/repos/:owner/:repo/labels/:name"}, + {http.MethodPost, "/repos/:owner/:repo/labels"}, + //{http.MethodPatch, "/repos/:owner/:repo/labels/:name"}, + {http.MethodDelete, "/repos/:owner/:repo/labels/:name"}, + {http.MethodGet, "/repos/:owner/:repo/issues/:number/labels"}, + {http.MethodPost, "/repos/:owner/:repo/issues/:number/labels"}, + {http.MethodDelete, "/repos/:owner/:repo/issues/:number/labels/:name"}, + {http.MethodPut, "/repos/:owner/:repo/issues/:number/labels"}, + {http.MethodDelete, "/repos/:owner/:repo/issues/:number/labels"}, + {http.MethodGet, "/repos/:owner/:repo/milestones/:number/labels"}, + {http.MethodGet, "/repos/:owner/:repo/milestones"}, + {http.MethodGet, "/repos/:owner/:repo/milestones/:number"}, + {http.MethodPost, "/repos/:owner/:repo/milestones"}, + //{http.MethodPatch, "/repos/:owner/:repo/milestones/:number"}, + {http.MethodDelete, "/repos/:owner/:repo/milestones/:number"}, // Miscellaneous - {"GET", "/emojis"}, - {"GET", "/gitignore/templates"}, - {"GET", "/gitignore/templates/:name"}, - {"POST", "/markdown"}, - {"POST", "/markdown/raw"}, - {"GET", "/meta"}, - {"GET", "/rate_limit"}, + {http.MethodGet, "/emojis"}, + {http.MethodGet, "/gitignore/templates"}, + {http.MethodGet, "/gitignore/templates/:name"}, + {http.MethodPost, "/markdown"}, + {http.MethodPost, "/markdown/raw"}, + {http.MethodGet, "/meta"}, + {http.MethodGet, "/rate_limit"}, // Organizations - {"GET", "/users/:user/orgs"}, - {"GET", "/user/orgs"}, - {"GET", "/orgs/:org"}, - //{"PATCH", "/orgs/:org"}, - {"GET", "/orgs/:org/members"}, - {"GET", "/orgs/:org/members/:user"}, - {"DELETE", "/orgs/:org/members/:user"}, - {"GET", "/orgs/:org/public_members"}, - {"GET", "/orgs/:org/public_members/:user"}, - {"PUT", "/orgs/:org/public_members/:user"}, - {"DELETE", "/orgs/:org/public_members/:user"}, - {"GET", "/orgs/:org/teams"}, - {"GET", "/teams/:id"}, - {"POST", "/orgs/:org/teams"}, - //{"PATCH", "/teams/:id"}, - {"DELETE", "/teams/:id"}, - {"GET", "/teams/:id/members"}, - {"GET", "/teams/:id/members/:user"}, - {"PUT", "/teams/:id/members/:user"}, - {"DELETE", "/teams/:id/members/:user"}, - {"GET", "/teams/:id/repos"}, - {"GET", "/teams/:id/repos/:owner/:repo"}, - {"PUT", "/teams/:id/repos/:owner/:repo"}, - {"DELETE", "/teams/:id/repos/:owner/:repo"}, - {"GET", "/user/teams"}, + {http.MethodGet, "/users/:user/orgs"}, + {http.MethodGet, "/user/orgs"}, + {http.MethodGet, "/orgs/:org"}, + //{http.MethodPatch, "/orgs/:org"}, + {http.MethodGet, "/orgs/:org/members"}, + {http.MethodGet, "/orgs/:org/members/:user"}, + {http.MethodDelete, "/orgs/:org/members/:user"}, + {http.MethodGet, "/orgs/:org/public_members"}, + {http.MethodGet, "/orgs/:org/public_members/:user"}, + {http.MethodPut, "/orgs/:org/public_members/:user"}, + {http.MethodDelete, "/orgs/:org/public_members/:user"}, + {http.MethodGet, "/orgs/:org/teams"}, + {http.MethodGet, "/teams/:id"}, + {http.MethodPost, "/orgs/:org/teams"}, + //{http.MethodPatch, "/teams/:id"}, + {http.MethodDelete, "/teams/:id"}, + {http.MethodGet, "/teams/:id/members"}, + {http.MethodGet, "/teams/:id/members/:user"}, + {http.MethodPut, "/teams/:id/members/:user"}, + {http.MethodDelete, "/teams/:id/members/:user"}, + {http.MethodGet, "/teams/:id/repos"}, + {http.MethodGet, "/teams/:id/repos/:owner/:repo"}, + {http.MethodPut, "/teams/:id/repos/:owner/:repo"}, + {http.MethodDelete, "/teams/:id/repos/:owner/:repo"}, + {http.MethodGet, "/user/teams"}, // Pull Requests - {"GET", "/repos/:owner/:repo/pulls"}, - {"GET", "/repos/:owner/:repo/pulls/:number"}, - {"POST", "/repos/:owner/:repo/pulls"}, - //{"PATCH", "/repos/:owner/:repo/pulls/:number"}, - {"GET", "/repos/:owner/:repo/pulls/:number/commits"}, - {"GET", "/repos/:owner/:repo/pulls/:number/files"}, - {"GET", "/repos/:owner/:repo/pulls/:number/merge"}, - {"PUT", "/repos/:owner/:repo/pulls/:number/merge"}, - {"GET", "/repos/:owner/:repo/pulls/:number/comments"}, - //{"GET", "/repos/:owner/:repo/pulls/comments"}, - //{"GET", "/repos/:owner/:repo/pulls/comments/:number"}, - {"PUT", "/repos/:owner/:repo/pulls/:number/comments"}, - //{"PATCH", "/repos/:owner/:repo/pulls/comments/:number"}, - //{"DELETE", "/repos/:owner/:repo/pulls/comments/:number"}, + {http.MethodGet, "/repos/:owner/:repo/pulls"}, + {http.MethodGet, "/repos/:owner/:repo/pulls/:number"}, + {http.MethodPost, "/repos/:owner/:repo/pulls"}, + //{http.MethodPatch, "/repos/:owner/:repo/pulls/:number"}, + {http.MethodGet, "/repos/:owner/:repo/pulls/:number/commits"}, + {http.MethodGet, "/repos/:owner/:repo/pulls/:number/files"}, + {http.MethodGet, "/repos/:owner/:repo/pulls/:number/merge"}, + {http.MethodPut, "/repos/:owner/:repo/pulls/:number/merge"}, + {http.MethodGet, "/repos/:owner/:repo/pulls/:number/comments"}, + //{http.MethodGet, "/repos/:owner/:repo/pulls/comments"}, + //{http.MethodGet, "/repos/:owner/:repo/pulls/comments/:number"}, + {http.MethodPut, "/repos/:owner/:repo/pulls/:number/comments"}, + //{http.MethodPatch, "/repos/:owner/:repo/pulls/comments/:number"}, + //{http.MethodDelete, "/repos/:owner/:repo/pulls/comments/:number"}, // Repositories - {"GET", "/user/repos"}, - {"GET", "/users/:user/repos"}, - {"GET", "/orgs/:org/repos"}, - {"GET", "/repositories"}, - {"POST", "/user/repos"}, - {"POST", "/orgs/:org/repos"}, - {"GET", "/repos/:owner/:repo"}, - //{"PATCH", "/repos/:owner/:repo"}, - {"GET", "/repos/:owner/:repo/contributors"}, - {"GET", "/repos/:owner/:repo/languages"}, - {"GET", "/repos/:owner/:repo/teams"}, - {"GET", "/repos/:owner/:repo/tags"}, - {"GET", "/repos/:owner/:repo/branches"}, - {"GET", "/repos/:owner/:repo/branches/:branch"}, - {"DELETE", "/repos/:owner/:repo"}, - {"GET", "/repos/:owner/:repo/collaborators"}, - {"GET", "/repos/:owner/:repo/collaborators/:user"}, - {"PUT", "/repos/:owner/:repo/collaborators/:user"}, - {"DELETE", "/repos/:owner/:repo/collaborators/:user"}, - {"GET", "/repos/:owner/:repo/comments"}, - {"GET", "/repos/:owner/:repo/commits/:sha/comments"}, - {"POST", "/repos/:owner/:repo/commits/:sha/comments"}, - {"GET", "/repos/:owner/:repo/comments/:id"}, - //{"PATCH", "/repos/:owner/:repo/comments/:id"}, - {"DELETE", "/repos/:owner/:repo/comments/:id"}, - {"GET", "/repos/:owner/:repo/commits"}, - {"GET", "/repos/:owner/:repo/commits/:sha"}, - {"GET", "/repos/:owner/:repo/readme"}, - //{"GET", "/repos/:owner/:repo/contents/*path"}, - //{"PUT", "/repos/:owner/:repo/contents/*path"}, - //{"DELETE", "/repos/:owner/:repo/contents/*path"}, - //{"GET", "/repos/:owner/:repo/:archive_format/:ref"}, - {"GET", "/repos/:owner/:repo/keys"}, - {"GET", "/repos/:owner/:repo/keys/:id"}, - {"POST", "/repos/:owner/:repo/keys"}, - //{"PATCH", "/repos/:owner/:repo/keys/:id"}, - {"DELETE", "/repos/:owner/:repo/keys/:id"}, - {"GET", "/repos/:owner/:repo/downloads"}, - {"GET", "/repos/:owner/:repo/downloads/:id"}, - {"DELETE", "/repos/:owner/:repo/downloads/:id"}, - {"GET", "/repos/:owner/:repo/forks"}, - {"POST", "/repos/:owner/:repo/forks"}, - {"GET", "/repos/:owner/:repo/hooks"}, - {"GET", "/repos/:owner/:repo/hooks/:id"}, - {"POST", "/repos/:owner/:repo/hooks"}, - //{"PATCH", "/repos/:owner/:repo/hooks/:id"}, - {"POST", "/repos/:owner/:repo/hooks/:id/tests"}, - {"DELETE", "/repos/:owner/:repo/hooks/:id"}, - {"POST", "/repos/:owner/:repo/merges"}, - {"GET", "/repos/:owner/:repo/releases"}, - {"GET", "/repos/:owner/:repo/releases/:id"}, - {"POST", "/repos/:owner/:repo/releases"}, - //{"PATCH", "/repos/:owner/:repo/releases/:id"}, - {"DELETE", "/repos/:owner/:repo/releases/:id"}, - {"GET", "/repos/:owner/:repo/releases/:id/assets"}, - {"GET", "/repos/:owner/:repo/stats/contributors"}, - {"GET", "/repos/:owner/:repo/stats/commit_activity"}, - {"GET", "/repos/:owner/:repo/stats/code_frequency"}, - {"GET", "/repos/:owner/:repo/stats/participation"}, - {"GET", "/repos/:owner/:repo/stats/punch_card"}, - {"GET", "/repos/:owner/:repo/statuses/:ref"}, - {"POST", "/repos/:owner/:repo/statuses/:ref"}, + {http.MethodGet, "/user/repos"}, + {http.MethodGet, "/users/:user/repos"}, + {http.MethodGet, "/orgs/:org/repos"}, + {http.MethodGet, "/repositories"}, + {http.MethodPost, "/user/repos"}, + {http.MethodPost, "/orgs/:org/repos"}, + {http.MethodGet, "/repos/:owner/:repo"}, + //{http.MethodPatch, "/repos/:owner/:repo"}, + {http.MethodGet, "/repos/:owner/:repo/contributors"}, + {http.MethodGet, "/repos/:owner/:repo/languages"}, + {http.MethodGet, "/repos/:owner/:repo/teams"}, + {http.MethodGet, "/repos/:owner/:repo/tags"}, + {http.MethodGet, "/repos/:owner/:repo/branches"}, + {http.MethodGet, "/repos/:owner/:repo/branches/:branch"}, + {http.MethodDelete, "/repos/:owner/:repo"}, + {http.MethodGet, "/repos/:owner/:repo/collaborators"}, + {http.MethodGet, "/repos/:owner/:repo/collaborators/:user"}, + {http.MethodPut, "/repos/:owner/:repo/collaborators/:user"}, + {http.MethodDelete, "/repos/:owner/:repo/collaborators/:user"}, + {http.MethodGet, "/repos/:owner/:repo/comments"}, + {http.MethodGet, "/repos/:owner/:repo/commits/:sha/comments"}, + {http.MethodPost, "/repos/:owner/:repo/commits/:sha/comments"}, + {http.MethodGet, "/repos/:owner/:repo/comments/:id"}, + //{http.MethodPatch, "/repos/:owner/:repo/comments/:id"}, + {http.MethodDelete, "/repos/:owner/:repo/comments/:id"}, + {http.MethodGet, "/repos/:owner/:repo/commits"}, + {http.MethodGet, "/repos/:owner/:repo/commits/:sha"}, + {http.MethodGet, "/repos/:owner/:repo/readme"}, + //{http.MethodGet, "/repos/:owner/:repo/contents/*path"}, + //{http.MethodPut, "/repos/:owner/:repo/contents/*path"}, + //{http.MethodDelete, "/repos/:owner/:repo/contents/*path"}, + //{http.MethodGet, "/repos/:owner/:repo/:archive_format/:ref"}, + {http.MethodGet, "/repos/:owner/:repo/keys"}, + {http.MethodGet, "/repos/:owner/:repo/keys/:id"}, + {http.MethodPost, "/repos/:owner/:repo/keys"}, + //{http.MethodPatch, "/repos/:owner/:repo/keys/:id"}, + {http.MethodDelete, "/repos/:owner/:repo/keys/:id"}, + {http.MethodGet, "/repos/:owner/:repo/downloads"}, + {http.MethodGet, "/repos/:owner/:repo/downloads/:id"}, + {http.MethodDelete, "/repos/:owner/:repo/downloads/:id"}, + {http.MethodGet, "/repos/:owner/:repo/forks"}, + {http.MethodPost, "/repos/:owner/:repo/forks"}, + {http.MethodGet, "/repos/:owner/:repo/hooks"}, + {http.MethodGet, "/repos/:owner/:repo/hooks/:id"}, + {http.MethodPost, "/repos/:owner/:repo/hooks"}, + //{http.MethodPatch, "/repos/:owner/:repo/hooks/:id"}, + {http.MethodPost, "/repos/:owner/:repo/hooks/:id/tests"}, + {http.MethodDelete, "/repos/:owner/:repo/hooks/:id"}, + {http.MethodPost, "/repos/:owner/:repo/merges"}, + {http.MethodGet, "/repos/:owner/:repo/releases"}, + {http.MethodGet, "/repos/:owner/:repo/releases/:id"}, + {http.MethodPost, "/repos/:owner/:repo/releases"}, + //{http.MethodPatch, "/repos/:owner/:repo/releases/:id"}, + {http.MethodDelete, "/repos/:owner/:repo/releases/:id"}, + {http.MethodGet, "/repos/:owner/:repo/releases/:id/assets"}, + {http.MethodGet, "/repos/:owner/:repo/stats/contributors"}, + {http.MethodGet, "/repos/:owner/:repo/stats/commit_activity"}, + {http.MethodGet, "/repos/:owner/:repo/stats/code_frequency"}, + {http.MethodGet, "/repos/:owner/:repo/stats/participation"}, + {http.MethodGet, "/repos/:owner/:repo/stats/punch_card"}, + {http.MethodGet, "/repos/:owner/:repo/statuses/:ref"}, + {http.MethodPost, "/repos/:owner/:repo/statuses/:ref"}, // Search - {"GET", "/search/repositories"}, - {"GET", "/search/code"}, - {"GET", "/search/issues"}, - {"GET", "/search/users"}, - {"GET", "/legacy/issues/search/:owner/:repository/:state/:keyword"}, - {"GET", "/legacy/repos/search/:keyword"}, - {"GET", "/legacy/user/search/:keyword"}, - {"GET", "/legacy/user/email/:email"}, + {http.MethodGet, "/search/repositories"}, + {http.MethodGet, "/search/code"}, + {http.MethodGet, "/search/issues"}, + {http.MethodGet, "/search/users"}, + {http.MethodGet, "/legacy/issues/search/:owner/:repository/:state/:keyword"}, + {http.MethodGet, "/legacy/repos/search/:keyword"}, + {http.MethodGet, "/legacy/user/search/:keyword"}, + {http.MethodGet, "/legacy/user/email/:email"}, // Users - {"GET", "/users/:user"}, - {"GET", "/user"}, - //{"PATCH", "/user"}, - {"GET", "/users"}, - {"GET", "/user/emails"}, - {"POST", "/user/emails"}, - {"DELETE", "/user/emails"}, - {"GET", "/users/:user/followers"}, - {"GET", "/user/followers"}, - {"GET", "/users/:user/following"}, - {"GET", "/user/following"}, - {"GET", "/user/following/:user"}, - {"GET", "/users/:user/following/:target_user"}, - {"PUT", "/user/following/:user"}, - {"DELETE", "/user/following/:user"}, - {"GET", "/users/:user/keys"}, - {"GET", "/user/keys"}, - {"GET", "/user/keys/:id"}, - {"POST", "/user/keys"}, - //{"PATCH", "/user/keys/:id"}, - {"DELETE", "/user/keys/:id"}, + {http.MethodGet, "/users/:user"}, + {http.MethodGet, "/user"}, + //{http.MethodPatch, "/user"}, + {http.MethodGet, "/users"}, + {http.MethodGet, "/user/emails"}, + {http.MethodPost, "/user/emails"}, + {http.MethodDelete, "/user/emails"}, + {http.MethodGet, "/users/:user/followers"}, + {http.MethodGet, "/user/followers"}, + {http.MethodGet, "/users/:user/following"}, + {http.MethodGet, "/user/following"}, + {http.MethodGet, "/user/following/:user"}, + {http.MethodGet, "/users/:user/following/:target_user"}, + {http.MethodPut, "/user/following/:user"}, + {http.MethodDelete, "/user/following/:user"}, + {http.MethodGet, "/users/:user/keys"}, + {http.MethodGet, "/user/keys"}, + {http.MethodGet, "/user/keys/:id"}, + {http.MethodPost, "/user/keys"}, + //{http.MethodPatch, "/user/keys/:id"}, + {http.MethodDelete, "/user/keys/:id"}, } func TestShouldBindUri(t *testing.T) { @@ -293,7 +293,7 @@ func TestShouldBindUri(t *testing.T) { Name string `uri:"name" binding:"required"` Id string `uri:"id" binding:"required"` } - router.Handle("GET", "/rest/:name/:id", func(c *Context) { + router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { var person Person assert.NoError(t, c.ShouldBindUri(&person)) assert.True(t, "" != person.Name) @@ -302,7 +302,7 @@ func TestShouldBindUri(t *testing.T) { }) path, _ := exampleFromPath("/rest/:name/:id") - w := performRequest(router, "GET", path) + w := performRequest(router, http.MethodGet, path) assert.Equal(t, "ShouldBindUri test OK", w.Body.String()) assert.Equal(t, http.StatusOK, w.Code) } @@ -315,7 +315,7 @@ func TestBindUri(t *testing.T) { Name string `uri:"name" binding:"required"` Id string `uri:"id" binding:"required"` } - router.Handle("GET", "/rest/:name/:id", func(c *Context) { + router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { var person Person assert.NoError(t, c.BindUri(&person)) assert.True(t, "" != person.Name) @@ -324,7 +324,7 @@ func TestBindUri(t *testing.T) { }) path, _ := exampleFromPath("/rest/:name/:id") - w := performRequest(router, "GET", path) + w := performRequest(router, http.MethodGet, path) assert.Equal(t, "BindUri test OK", w.Body.String()) assert.Equal(t, http.StatusOK, w.Code) } @@ -336,13 +336,13 @@ func TestBindUriError(t *testing.T) { type Member struct { Number string `uri:"num" binding:"required,uuid"` } - router.Handle("GET", "/new/rest/:num", func(c *Context) { + router.Handle(http.MethodGet, "/new/rest/:num", func(c *Context) { var m Member assert.Error(t, c.BindUri(&m)) }) path1, _ := exampleFromPath("/new/rest/:num") - w1 := performRequest(router, "GET", path1) + w1 := performRequest(router, http.MethodGet, path1) assert.Equal(t, http.StatusBadRequest, w1.Code) } @@ -358,7 +358,7 @@ func TestRaceContextCopy(t *testing.T) { go readWriteKeys(c.Copy()) c.String(http.StatusOK, "run OK, no panics") }) - w := performRequest(router, "GET", "/test/copy/race") + w := performRequest(router, http.MethodGet, "/test/copy/race") assert.Equal(t, "run OK, no panics", w.Body.String()) } @@ -438,7 +438,7 @@ func exampleFromPath(path string) (string, Params) { func BenchmarkGithub(b *testing.B) { router := New() githubConfigRouter(router) - runRequest(b, router, "GET", "/legacy/issues/search/:owner/:repository/:state/:keyword") + runRequest(b, router, http.MethodGet, "/legacy/issues/search/:owner/:repository/:state/:keyword") } func BenchmarkParallelGithub(b *testing.B) { @@ -446,7 +446,7 @@ func BenchmarkParallelGithub(b *testing.B) { router := New() githubConfigRouter(router) - req, _ := http.NewRequest("POST", "/repos/manucorporat/sse/git/blobs", nil) + req, _ := http.NewRequest(http.MethodPost, "/repos/manucorporat/sse/git/blobs", nil) b.RunParallel(func(pb *testing.PB) { // Each goroutine has its own bytes.Buffer. @@ -462,7 +462,7 @@ func BenchmarkParallelGithubDefault(b *testing.B) { router := New() githubConfigRouter(router) - req, _ := http.NewRequest("POST", "/repos/manucorporat/sse/git/blobs", nil) + req, _ := http.NewRequest(http.MethodPost, "/repos/manucorporat/sse/git/blobs", nil) b.RunParallel(func(pb *testing.PB) { // Each goroutine has its own bytes.Buffer. diff --git a/logger.go b/logger.go index fcf90c25..d5b96b3e 100644 --- a/logger.go +++ b/logger.go @@ -99,19 +99,19 @@ func (p *LogFormatterParams) MethodColor() string { method := p.Method switch method { - case "GET": + case http.MethodGet: return blue - case "POST": + case http.MethodPost: return cyan - case "PUT": + case http.MethodPut: return yellow - case "DELETE": + case http.MethodDelete: return red - case "PATCH": + case http.MethodPatch: return green - case "HEAD": + case http.MethodHead: return magenta - case "OPTIONS": + case http.MethodOptions: return white default: return reset diff --git a/routergroup.go b/routergroup.go index 2e7a5b90..9ff7c038 100644 --- a/routergroup.go +++ b/routergroup.go @@ -95,51 +95,51 @@ func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...Ha // 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) + return group.handle(http.MethodPost, relativePath, handlers) } // 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) + return group.handle(http.MethodGet, relativePath, handlers) } // 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) + return group.handle(http.MethodDelete, relativePath, handlers) } // 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) + return group.handle(http.MethodPatch, relativePath, handlers) } // 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) + return group.handle(http.MethodPut, relativePath, handlers) } // 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) + return group.handle(http.MethodOptions, relativePath, handlers) } // 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) + return group.handle(http.MethodHead, relativePath, handlers) } // Any registers a route that matches all the HTTP methods. // GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE. func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes { - group.handle("GET", relativePath, handlers) - group.handle("POST", relativePath, handlers) - group.handle("PUT", relativePath, handlers) - group.handle("PATCH", relativePath, handlers) - group.handle("HEAD", relativePath, handlers) - group.handle("OPTIONS", relativePath, handlers) - group.handle("DELETE", relativePath, handlers) - group.handle("CONNECT", relativePath, handlers) - group.handle("TRACE", relativePath, handlers) + group.handle(http.MethodGet, relativePath, handlers) + group.handle(http.MethodPost, relativePath, handlers) + group.handle(http.MethodPut, relativePath, handlers) + group.handle(http.MethodPatch, relativePath, handlers) + group.handle(http.MethodHead, relativePath, handlers) + group.handle(http.MethodOptions, relativePath, handlers) + group.handle(http.MethodDelete, relativePath, handlers) + group.handle(http.MethodConnect, relativePath, handlers) + group.handle(http.MethodTrace, relativePath, handlers) return group.returnObj() } diff --git a/routergroup_test.go b/routergroup_test.go index ce3d54a2..0e49d65b 100644 --- a/routergroup_test.go +++ b/routergroup_test.go @@ -33,13 +33,13 @@ func TestRouterGroupBasic(t *testing.T) { } func TestRouterGroupBasicHandle(t *testing.T) { - performRequestInGroup(t, "GET") - performRequestInGroup(t, "POST") - performRequestInGroup(t, "PUT") - performRequestInGroup(t, "PATCH") - performRequestInGroup(t, "DELETE") - performRequestInGroup(t, "HEAD") - performRequestInGroup(t, "OPTIONS") + performRequestInGroup(t, http.MethodGet) + performRequestInGroup(t, http.MethodPost) + performRequestInGroup(t, http.MethodPut) + performRequestInGroup(t, http.MethodPatch) + performRequestInGroup(t, http.MethodDelete) + performRequestInGroup(t, http.MethodHead) + performRequestInGroup(t, http.MethodOptions) } func performRequestInGroup(t *testing.T, method string) { @@ -55,25 +55,25 @@ func performRequestInGroup(t *testing.T, method string) { } switch method { - case "GET": + case http.MethodGet: v1.GET("/test", handler) login.GET("/test", handler) - case "POST": + case http.MethodPost: v1.POST("/test", handler) login.POST("/test", handler) - case "PUT": + case http.MethodPut: v1.PUT("/test", handler) login.PUT("/test", handler) - case "PATCH": + case http.MethodPatch: v1.PATCH("/test", handler) login.PATCH("/test", handler) - case "DELETE": + case http.MethodDelete: v1.DELETE("/test", handler) login.DELETE("/test", handler) - case "HEAD": + case http.MethodHead: v1.HEAD("/test", handler) login.HEAD("/test", handler) - case "OPTIONS": + case http.MethodOptions: v1.OPTIONS("/test", handler) login.OPTIONS("/test", handler) default: @@ -128,7 +128,7 @@ func TestRouterGroupTooManyHandlers(t *testing.T) { func TestRouterGroupBadMethod(t *testing.T) { router := New() assert.Panics(t, func() { - router.Handle("get", "/") + router.Handle(http.MethodGet, "/") }) assert.Panics(t, func() { router.Handle(" GET", "/") @@ -162,7 +162,7 @@ func testRoutesInterface(t *testing.T, r IRoutes) { handler := func(c *Context) {} assert.Equal(t, r, r.Use(handler)) - assert.Equal(t, r, r.Handle("GET", "/handler", handler)) + assert.Equal(t, r, r.Handle(http.MethodGet, "/handler", handler)) assert.Equal(t, r, r.Any("/any", handler)) assert.Equal(t, r, r.GET("/", handler)) assert.Equal(t, r, r.POST("/", handler)) diff --git a/routes_test.go b/routes_test.go index d1ddbe91..ee6ea823 100644 --- a/routes_test.go +++ b/routes_test.go @@ -70,10 +70,10 @@ func testRouteNotOK2(method string, t *testing.T) { router := New() router.HandleMethodNotAllowed = true var methodRoute string - if method == "POST" { - methodRoute = "GET" + if method == http.MethodPost { + methodRoute = http.MethodGet } else { - methodRoute = "POST" + methodRoute = http.MethodPost } router.Handle(methodRoute, "/test", func(c *Context) { passed = true @@ -99,46 +99,46 @@ func TestRouterMethod(t *testing.T) { c.String(http.StatusOK, "sup3") }) - w := performRequest(router, "PUT", "/hey") + w := performRequest(router, http.MethodPut, "/hey") assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "called", w.Body.String()) } func TestRouterGroupRouteOK(t *testing.T) { - testRouteOK("GET", t) - testRouteOK("POST", t) - testRouteOK("PUT", t) - testRouteOK("PATCH", t) - testRouteOK("HEAD", t) - testRouteOK("OPTIONS", t) - testRouteOK("DELETE", t) - testRouteOK("CONNECT", t) - testRouteOK("TRACE", t) + testRouteOK(http.MethodGet, t) + testRouteOK(http.MethodPost, t) + testRouteOK(http.MethodPut, t) + testRouteOK(http.MethodPatch, t) + testRouteOK(http.MethodHead, t) + testRouteOK(http.MethodOptions, t) + testRouteOK(http.MethodDelete, t) + testRouteOK(http.MethodConnect, t) + testRouteOK(http.MethodTrace, t) } func TestRouteNotOK(t *testing.T) { - testRouteNotOK("GET", t) - testRouteNotOK("POST", t) - testRouteNotOK("PUT", t) - testRouteNotOK("PATCH", t) - testRouteNotOK("HEAD", t) - testRouteNotOK("OPTIONS", t) - testRouteNotOK("DELETE", t) - testRouteNotOK("CONNECT", t) - testRouteNotOK("TRACE", t) + testRouteNotOK(http.MethodGet, t) + testRouteNotOK(http.MethodPost, t) + testRouteNotOK(http.MethodPut, t) + testRouteNotOK(http.MethodPatch, t) + testRouteNotOK(http.MethodHead, t) + testRouteNotOK(http.MethodOptions, t) + testRouteNotOK(http.MethodDelete, t) + testRouteNotOK(http.MethodConnect, t) + testRouteNotOK(http.MethodTrace, t) } func TestRouteNotOK2(t *testing.T) { - testRouteNotOK2("GET", t) - testRouteNotOK2("POST", t) - testRouteNotOK2("PUT", t) - testRouteNotOK2("PATCH", t) - testRouteNotOK2("HEAD", t) - testRouteNotOK2("OPTIONS", t) - testRouteNotOK2("DELETE", t) - testRouteNotOK2("CONNECT", t) - testRouteNotOK2("TRACE", t) + testRouteNotOK2(http.MethodGet, t) + testRouteNotOK2(http.MethodPost, t) + testRouteNotOK2(http.MethodPut, t) + testRouteNotOK2(http.MethodPatch, t) + testRouteNotOK2(http.MethodHead, t) + testRouteNotOK2(http.MethodOptions, t) + testRouteNotOK2(http.MethodDelete, t) + testRouteNotOK2(http.MethodConnect, t) + testRouteNotOK2(http.MethodTrace, t) } func TestRouteRedirectTrailingSlash(t *testing.T) { @@ -150,50 +150,50 @@ func TestRouteRedirectTrailingSlash(t *testing.T) { router.POST("/path3", func(c *Context) {}) router.PUT("/path4/", func(c *Context) {}) - w := performRequest(router, "GET", "/path/") + w := performRequest(router, http.MethodGet, "/path/") assert.Equal(t, "/path", w.Header().Get("Location")) assert.Equal(t, http.StatusMovedPermanently, w.Code) - w = performRequest(router, "GET", "/path2") + w = performRequest(router, http.MethodGet, "/path2") assert.Equal(t, "/path2/", w.Header().Get("Location")) assert.Equal(t, http.StatusMovedPermanently, w.Code) - w = performRequest(router, "POST", "/path3/") + w = performRequest(router, http.MethodPost, "/path3/") assert.Equal(t, "/path3", w.Header().Get("Location")) assert.Equal(t, http.StatusTemporaryRedirect, w.Code) - w = performRequest(router, "PUT", "/path4") + w = performRequest(router, http.MethodPut, "/path4") assert.Equal(t, "/path4/", w.Header().Get("Location")) assert.Equal(t, http.StatusTemporaryRedirect, w.Code) - w = performRequest(router, "GET", "/path") + w = performRequest(router, http.MethodGet, "/path") assert.Equal(t, http.StatusOK, w.Code) - w = performRequest(router, "GET", "/path2/") + w = performRequest(router, http.MethodGet, "/path2/") assert.Equal(t, http.StatusOK, w.Code) - w = performRequest(router, "POST", "/path3") + w = performRequest(router, http.MethodPost, "/path3") assert.Equal(t, http.StatusOK, w.Code) - w = performRequest(router, "PUT", "/path4/") + w = performRequest(router, http.MethodPut, "/path4/") assert.Equal(t, http.StatusOK, w.Code) - w = performRequest(router, "GET", "/path2", header{Key: "X-Forwarded-Prefix", Value: "/api"}) + w = performRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/api"}) assert.Equal(t, "/api/path2/", w.Header().Get("Location")) assert.Equal(t, 301, w.Code) - w = performRequest(router, "GET", "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"}) + w = performRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"}) assert.Equal(t, 200, w.Code) router.RedirectTrailingSlash = false - w = performRequest(router, "GET", "/path/") + w = performRequest(router, http.MethodGet, "/path/") assert.Equal(t, http.StatusNotFound, w.Code) - w = performRequest(router, "GET", "/path2") + w = performRequest(router, http.MethodGet, "/path2") assert.Equal(t, http.StatusNotFound, w.Code) - w = performRequest(router, "POST", "/path3/") + w = performRequest(router, http.MethodPost, "/path3/") assert.Equal(t, http.StatusNotFound, w.Code) - w = performRequest(router, "PUT", "/path4") + w = performRequest(router, http.MethodPut, "/path4") assert.Equal(t, http.StatusNotFound, w.Code) } @@ -207,19 +207,19 @@ func TestRouteRedirectFixedPath(t *testing.T) { router.POST("/PATH3", func(c *Context) {}) router.POST("/Path4/", func(c *Context) {}) - w := performRequest(router, "GET", "/PATH") + w := performRequest(router, http.MethodGet, "/PATH") assert.Equal(t, "/path", w.Header().Get("Location")) assert.Equal(t, http.StatusMovedPermanently, w.Code) - w = performRequest(router, "GET", "/path2") + w = performRequest(router, http.MethodGet, "/path2") assert.Equal(t, "/Path2", w.Header().Get("Location")) assert.Equal(t, http.StatusMovedPermanently, w.Code) - w = performRequest(router, "POST", "/path3") + w = performRequest(router, http.MethodPost, "/path3") assert.Equal(t, "/PATH3", w.Header().Get("Location")) assert.Equal(t, http.StatusTemporaryRedirect, w.Code) - w = performRequest(router, "POST", "/path4") + w = performRequest(router, http.MethodPost, "/path4") assert.Equal(t, "/Path4/", w.Header().Get("Location")) assert.Equal(t, http.StatusTemporaryRedirect, w.Code) } @@ -249,7 +249,7 @@ func TestRouteParamsByName(t *testing.T) { assert.False(t, ok) }) - w := performRequest(router, "GET", "/test/john/smith/is/super/great") + w := performRequest(router, http.MethodGet, "/test/john/smith/is/super/great") assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "john", name) @@ -283,7 +283,7 @@ func TestRouteParamsByNameWithExtraSlash(t *testing.T) { assert.False(t, ok) }) - w := performRequest(router, "GET", "//test//john//smith//is//super//great") + w := performRequest(router, http.MethodGet, "//test//john//smith//is//super//great") assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "john", name) @@ -311,16 +311,16 @@ func TestRouteStaticFile(t *testing.T) { router.Static("/using_static", dir) router.StaticFile("/result", f.Name()) - w := performRequest(router, "GET", "/using_static/"+filename) - w2 := performRequest(router, "GET", "/result") + w := performRequest(router, http.MethodGet, "/using_static/"+filename) + w2 := performRequest(router, http.MethodGet, "/result") assert.Equal(t, w, w2) assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "Gin Web Framework", w.Body.String()) assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) - w3 := performRequest(router, "HEAD", "/using_static/"+filename) - w4 := performRequest(router, "HEAD", "/result") + w3 := performRequest(router, http.MethodHead, "/using_static/"+filename) + w4 := performRequest(router, http.MethodHead, "/result") assert.Equal(t, w3, w4) assert.Equal(t, http.StatusOK, w3.Code) @@ -331,7 +331,7 @@ func TestRouteStaticListingDir(t *testing.T) { router := New() router.StaticFS("/", Dir("./", true)) - w := performRequest(router, "GET", "/") + w := performRequest(router, http.MethodGet, "/") assert.Equal(t, http.StatusOK, w.Code) assert.Contains(t, w.Body.String(), "gin.go") @@ -343,7 +343,7 @@ func TestRouteStaticNoListing(t *testing.T) { router := New() router.Static("/", "./") - w := performRequest(router, "GET", "/") + w := performRequest(router, http.MethodGet, "/") assert.Equal(t, http.StatusNotFound, w.Code) assert.NotContains(t, w.Body.String(), "gin.go") @@ -358,7 +358,7 @@ func TestRouterMiddlewareAndStatic(t *testing.T) { }) static.Static("/", "./") - w := performRequest(router, "GET", "/gin.go") + w := performRequest(router, http.MethodGet, "/gin.go") assert.Equal(t, http.StatusOK, w.Code) assert.Contains(t, w.Body.String(), "package gin") @@ -372,13 +372,13 @@ func TestRouteNotAllowedEnabled(t *testing.T) { router := New() router.HandleMethodNotAllowed = true router.POST("/path", func(c *Context) {}) - w := performRequest(router, "GET", "/path") + w := performRequest(router, http.MethodGet, "/path") assert.Equal(t, http.StatusMethodNotAllowed, w.Code) router.NoMethod(func(c *Context) { c.String(http.StatusTeapot, "responseText") }) - w = performRequest(router, "GET", "/path") + w = performRequest(router, http.MethodGet, "/path") assert.Equal(t, "responseText", w.Body.String()) assert.Equal(t, http.StatusTeapot, w.Code) } @@ -387,9 +387,9 @@ func TestRouteNotAllowedEnabled2(t *testing.T) { router := New() router.HandleMethodNotAllowed = true // add one methodTree to trees - router.addRoute("POST", "/", HandlersChain{func(_ *Context) {}}) + router.addRoute(http.MethodPost, "/", HandlersChain{func(_ *Context) {}}) router.GET("/path2", func(c *Context) {}) - w := performRequest(router, "POST", "/path2") + w := performRequest(router, http.MethodPost, "/path2") assert.Equal(t, http.StatusMethodNotAllowed, w.Code) } @@ -397,13 +397,13 @@ func TestRouteNotAllowedDisabled(t *testing.T) { router := New() router.HandleMethodNotAllowed = false router.POST("/path", func(c *Context) {}) - w := performRequest(router, "GET", "/path") + w := performRequest(router, http.MethodGet, "/path") assert.Equal(t, http.StatusNotFound, w.Code) router.NoMethod(func(c *Context) { c.String(http.StatusTeapot, "responseText") }) - w = performRequest(router, "GET", "/path") + w = performRequest(router, http.MethodGet, "/path") assert.Equal(t, "404 page not found", w.Body.String()) assert.Equal(t, http.StatusNotFound, w.Code) } @@ -453,7 +453,7 @@ func TestRouterNotFound(t *testing.T) { {"/nope", http.StatusNotFound, ""}, // NotFound } for _, tr := range testRoutes { - w := performRequest(router, "GET", tr.route) + w := performRequest(router, http.MethodGet, tr.route) assert.Equal(t, tr.code, w.Code) if w.Code != http.StatusNotFound { assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) @@ -466,20 +466,20 @@ func TestRouterNotFound(t *testing.T) { c.AbortWithStatus(http.StatusNotFound) notFound = true }) - w := performRequest(router, "GET", "/nope") + w := performRequest(router, http.MethodGet, "/nope") assert.Equal(t, http.StatusNotFound, w.Code) assert.True(t, notFound) // Test other method than GET (want 307 instead of 301) router.PATCH("/path", func(c *Context) {}) - w = performRequest(router, "PATCH", "/path/") + w = performRequest(router, http.MethodPatch, "/path/") assert.Equal(t, http.StatusTemporaryRedirect, w.Code) assert.Equal(t, "map[Location:[/path]]", fmt.Sprint(w.Header())) // Test special case where no node for the prefix "/" exists router = New() router.GET("/a", func(c *Context) {}) - w = performRequest(router, "GET", "/") + w = performRequest(router, http.MethodGet, "/") assert.Equal(t, http.StatusNotFound, w.Code) } @@ -490,10 +490,10 @@ func TestRouterStaticFSNotFound(t *testing.T) { c.String(404, "non existent") }) - w := performRequest(router, "GET", "/nonexistent") + w := performRequest(router, http.MethodGet, "/nonexistent") assert.Equal(t, "non existent", w.Body.String()) - w = performRequest(router, "HEAD", "/nonexistent") + w = performRequest(router, http.MethodHead, "/nonexistent") assert.Equal(t, "non existent", w.Body.String()) } @@ -503,7 +503,7 @@ func TestRouterStaticFSFileNotFound(t *testing.T) { router.StaticFS("/", http.FileSystem(http.Dir("."))) assert.NotPanics(t, func() { - performRequest(router, "GET", "/nonexistent") + performRequest(router, http.MethodGet, "/nonexistent") }) } @@ -520,11 +520,11 @@ func TestMiddlewareCalledOnceByRouterStaticFSNotFound(t *testing.T) { router.StaticFS("/", http.FileSystem(http.Dir("/thisreallydoesntexist/"))) // First access - performRequest(router, "GET", "/nonexistent") + performRequest(router, http.MethodGet, "/nonexistent") assert.Equal(t, 1, middlewareCalledNum) // Second access - performRequest(router, "HEAD", "/nonexistent") + performRequest(router, http.MethodHead, "/nonexistent") assert.Equal(t, 2, middlewareCalledNum) } @@ -543,7 +543,7 @@ func TestRouteRawPath(t *testing.T) { assert.Equal(t, "222", num) }) - w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/222") + w := performRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/222") assert.Equal(t, http.StatusOK, w.Code) } @@ -563,7 +563,7 @@ func TestRouteRawPathNoUnescape(t *testing.T) { assert.Equal(t, "333", num) }) - w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/333") + w := performRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/333") assert.Equal(t, http.StatusOK, w.Code) } @@ -574,7 +574,7 @@ func TestRouteServeErrorWithWriteHeader(t *testing.T) { c.Next() }) - w := performRequest(route, "GET", "/NotFound") + w := performRequest(route, http.MethodGet, "/NotFound") assert.Equal(t, 421, w.Code) assert.Equal(t, 0, w.Body.Len()) } @@ -605,7 +605,7 @@ func TestRouteContextHoldsFullPath(t *testing.T) { } for _, route := range routes { - w := performRequest(router, "GET", route) + w := performRequest(router, http.MethodGet, route) assert.Equal(t, http.StatusOK, w.Code) } @@ -615,6 +615,6 @@ func TestRouteContextHoldsFullPath(t *testing.T) { assert.Equal(t, "", c.FullPath()) }) - w := performRequest(router, "GET", "/not-found") + w := performRequest(router, http.MethodGet, "/not-found") assert.Equal(t, http.StatusNotFound, w.Code) } From 3957f6bb4b84a86c6a6694b0b6af0b8496ae2207 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 29 Nov 2019 13:07:19 +0800 Subject: [PATCH 30/90] docs(benchmarks): for gin v1.5 (#2153) --- BENCHMARKS.md | 1135 ++++++++++++++++++++++++++----------------------- 1 file changed, 612 insertions(+), 523 deletions(-) diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 9a7df86a..e4ff4677 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -2,603 +2,692 @@ ## 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) +**Machine:** 12 CPU, 24 GB RAM. Ubuntu 16.04.2 x64 +**Date:** Nov 26th, 2019 +**Go Version:** 1.13.4 linux/amd64 +**Source:** [Go HTTP Router Benchmark](https://github.com/julienschmidt/go-http-routing-benchmark) +**Result:** [See the gist](https://gist.github.com/appleboy/b5f2ecfaf50824ae9c64dcfb9165ae5e) ## Static Routes: 157 ``` -Gin: 30512 Bytes +Gin: 34936 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 +HttpServeMux: 14512 Bytes +Ace: 30648 Bytes +Aero: 800696 Bytes +Bear: 30664 Bytes +Beego: 98456 Bytes +Bone: 40224 Bytes +Chi: 83608 Bytes +CloudyKitRouter: 30448 Bytes +Denco: 9928 Bytes +Echo: 76584 Bytes +GocraftWeb: 55496 Bytes +Goji: 29744 Bytes +Gojiv2: 105840 Bytes +GoJsonRest: 137512 Bytes +GoRestful: 816936 Bytes +GorillaMux: 585632 Bytes +GowwwRouter: 24968 Bytes +HttpRouter: 21680 Bytes +HttpTreeMux: 73448 Bytes +Kocha: 115472 Bytes +LARS: 30640 Bytes +Macaron: 38592 Bytes +Martini: 310864 Bytes +Pat: 19696 Bytes +Possum: 89920 Bytes +R2router: 23712 Bytes +Rivet: 24608 Bytes +Tango: 28264 Bytes +TigerTonic: 78768 Bytes +Traffic: 538976 Bytes +Vulcan: 369960 Bytes ``` ## GithubAPI Routes: 203 ``` -Gin: 52672 Bytes +Gin: 58512 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 +Ace: 48640 Bytes +Aero: 1386208 Bytes +Bear: 82536 Bytes +Beego: 150936 Bytes +Bone: 100976 Bytes +Chi: 95112 Bytes +CloudyKitRouter: 93704 Bytes +Denco: 36736 Bytes +Echo: 96328 Bytes +GocraftWeb: 95432 Bytes +Goji: 51600 Bytes +Gojiv2: 104704 Bytes +GoJsonRest: 142024 Bytes +GoRestful: 1241656 Bytes +GorillaMux: 1322784 Bytes +GowwwRouter: 80008 Bytes +HttpRouter: 37096 Bytes +HttpTreeMux: 78800 Bytes +Kocha: 785408 Bytes +LARS: 48600 Bytes +Macaron: 93680 Bytes +Martini: 485264 Bytes +Pat: 21200 Bytes +Possum: 85312 Bytes +R2router: 47104 Bytes +Rivet: 42840 Bytes +Tango: 54840 Bytes +TigerTonic: 96176 Bytes +Traffic: 921744 Bytes +Vulcan: 425368 Bytes ``` ## GPlusAPI Routes: 13 ``` -Gin: 3968 Bytes +Gin: 4384 Bytes -Ace: 3600 Bytes +Ace: 3 664 Bytes +Aero: 88248 Bytes Bear: 7112 Bytes -Beego: 10048 Bytes -Bone: 6480 Bytes -Denco: 3256 Bytes -Echo: 9000 Bytes +Beego: 10272 Bytes +Bone: 6688 Bytes +Chi: 8024 Bytes +CloudyKitRouter: 6728 Bytes +Denco: 3264 Bytes +Echo: 9272 Bytes GocraftWeb: 7496 Bytes -Goji: 2912 Bytes +Goji: 3152 Bytes Gojiv2: 7376 Bytes -GoJsonRest: 11544 Bytes -GoRestful: 88776 Bytes -GorillaMux: 71488 Bytes -HttpRouter: 2712 Bytes +GoJsonRest: 11416 Bytes +GoRestful: 74328 Bytes +GorillaMux: 66208 Bytes +GowwwRouter: 5744 Bytes +HttpRouter: 2760 Bytes HttpTreeMux: 7440 Bytes Kocha: 128880 Bytes -LARS: 3640 Bytes +LARS: 3656 Bytes Macaron: 8656 Bytes -Martini: 23936 Bytes +Martini: 23920 Bytes Pat: 1856 Bytes Possum: 7248 Bytes R2router: 3928 Bytes Rivet: 3064 Bytes -Tango: 4912 Bytes +Tango: 5168 Bytes TigerTonic: 9408 Bytes -Traffic: 49472 Bytes -Vulcan: 25496 Bytes +Traffic: 46400 Bytes +Vulcan: 25544 Bytes ``` ## ParseAPI Routes: 26 ``` -Gin: 6928 Bytes +Gin: 7776 Bytes -Ace: 6592 Bytes -Bear: 12320 Bytes -Beego: 18960 Bytes -Bone: 11024 Bytes -Denco: 4184 Bytes -Echo: 11168 Bytes +Ace: 6656 Bytes +Aero: 163736 Bytes +Bear: 12528 Bytes +Beego: 19280 Bytes +Bone: 11440 Bytes +Chi: 9744 Bytes +Denco: 4192 Bytes +Echo: 11648 Bytes GocraftWeb: 12800 Bytes -Goji: 5232 Bytes +Goji: 5680 Bytes Gojiv2: 14464 Bytes -GoJsonRest: 14216 Bytes -GoRestful: 127368 Bytes -GorillaMux: 123016 Bytes -HttpRouter: 4976 Bytes +GoJsonRest: 14424 Bytes +GoRestful: 116264 Bytes +GorillaMux: 105880 Bytes +GowwwRouter: 9344 Bytes +HttpRouter: 5024 Bytes HttpTreeMux: 7848 Bytes Kocha: 181712 Bytes LARS: 6632 Bytes Macaron: 13648 Bytes -Martini: 45952 Bytes +Martini: 45888 Bytes Pat: 2560 Bytes Possum: 9200 Bytes R2router: 7056 Bytes Rivet: 5680 Bytes -Tango: 8664 Bytes +Tango: 8920 Bytes TigerTonic: 9840 Bytes -Traffic: 93480 Bytes +Traffic: 79096 Bytes Vulcan: 44504 Bytes ``` ## Static Routes ``` -BenchmarkGin_StaticAll 50000 34506 ns/op 0 B/op 0 allocs/op +BenchmarkGin_StaticAll 25604 45487 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 +BenchmarkAce_StaticAll 28402 42046 ns/op 0 B/op 0 allocs/op +BenchmarkAero_StaticAll 38766 30333 ns/op 0 B/op 0 allocs/op +BenchmarkHttpServeMux_StaticAll 25728 46511 ns/op 0 B/op 0 allocs/op +BenchmarkBeego_StaticAll 5098 288527 ns/op 55264 B/op 471 allocs/op +BenchmarkBear_StaticAll 10000 126323 ns/op 20272 B/op 469 allocs/op +BenchmarkBone_StaticAll 9499 113631 ns/op 0 B/op 0 allocs/op +BenchmarkChi_StaticAll 7912 237363 ns/op 67824 B/op 471 allocs/op +BenchmarkCloudyKitRouter_StaticAll 41626 28668 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_StaticAll 95774 12221 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_StaticAll 26246 44603 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_StaticAll 10000 193337 ns/op 46312 B/op 785 allocs/op +BenchmarkGoji_StaticAll 15886 75789 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_StaticAll 1886 597374 ns/op 205984 B/op 1570 allocs/op +BenchmarkGoJsonRest_StaticAll 4700 307144 ns/op 51653 B/op 1727 allocs/op +BenchmarkGoRestful_StaticAll 429 2880165 ns/op 613280 B/op 2053 allocs/op +BenchmarkGorillaMux_StaticAll 754 1491761 ns/op 153233 B/op 1413 allocs/op +BenchmarkGowwwRouter_StaticAll 28071 42629 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_StaticAll 47672 24875 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_StaticAll 46770 25100 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_StaticAll 61045 19494 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_StaticAll 36103 32700 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_StaticAll 4261 430131 ns/op 115552 B/op 1256 allocs/op +BenchmarkMartini_StaticAll 481 2320157 ns/op 125444 B/op 1717 allocs/op +BenchmarkPat_StaticAll 325 3739521 ns/op 602832 B/op 12559 allocs/op +BenchmarkPossum_StaticAll 10000 203575 ns/op 65312 B/op 471 allocs/op +BenchmarkR2router_StaticAll 10000 110536 ns/op 22608 B/op 628 allocs/op +BenchmarkRivet_StaticAll 23344 51174 ns/op 0 B/op 0 allocs/op +BenchmarkTango_StaticAll 3596 340045 ns/op 39209 B/op 1256 allocs/op +BenchmarkTigerTonic_StaticAll 16784 71807 ns/op 7376 B/op 157 allocs/op +BenchmarkTraffic_StaticAll 350 3435155 ns/op 754862 B/op 14601 allocs/op +BenchmarkVulcan_StaticAll 5930 200284 ns/op 15386 B/op 471 allocs/op ``` ## Micro Benchmarks ``` -BenchmarkGin_Param 20000000 113 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Param 8623915 139 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_Param 3976539 290 ns/op 32 B/op 1 allocs/op +BenchmarkAero_Param 8948976 133 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Param 1000000 1277 ns/op 456 B/op 5 allocs/op +BenchmarkBeego_Param 889404 1785 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Param 1000000 2219 ns/op 816 B/op 6 allocs/op +BenchmarkChi_Param 1000000 1386 ns/op 432 B/op 3 allocs/op +BenchmarkCloudyKitRouter_Param 18343244 61.2 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_Param 5637424 204 ns/op 32 B/op 1 allocs/op +BenchmarkEcho_Param 9540910 122 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param 1000000 1939 ns/op 648 B/op 8 allocs/op +BenchmarkGoji_Param 1283509 938 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Param 331266 3554 ns/op 1328 B/op 11 allocs/op +BenchmarkGoJsonRest_Param 908851 2158 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_Param 135781 9339 ns/op 4192 B/op 14 allocs/op +BenchmarkGorillaMux_Param 308407 3893 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_Param 1000000 1044 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Param 6653476 162 ns/op 32 B/op 1 allocs/op +BenchmarkHttpTreeMux_Param 1361378 819 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_Param 3084330 353 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_Param 11502079 107 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param 439095 3750 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_Param 177099 7479 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_Param 729747 2048 ns/op 536 B/op 11 allocs/op +BenchmarkPossum_Param 995989 1705 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Param 1000000 1037 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Param 4057065 271 ns/op 48 B/op 1 allocs/op +BenchmarkTango_Param 812029 1682 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_Param 450592 3358 ns/op 776 B/op 16 allocs/op +BenchmarkTraffic_Param 206390 5661 ns/op 1856 B/op 21 allocs/op +BenchmarkVulcan_Param 1441147 792 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_Param5 1891473 632 ns/op 160 B/op 1 allocs/op +BenchmarkAero_Param5 5191258 227 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Param5 988882 1734 ns/op 501 B/op 5 allocs/op +BenchmarkBeego_Param5 625438 2132 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Param5 622030 3061 ns/op 864 B/op 6 allocs/op +BenchmarkChi_Param5 1000000 1735 ns/op 432 B/op 3 allocs/op +BenchmarkCloudyKitRouter_Param5 5167868 225 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_Param5 2174550 550 ns/op 160 B/op 1 allocs/op +BenchmarkEcho_Param5 4272258 275 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Param5 4190391 275 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param5 623739 3107 ns/op 920 B/op 11 allocs/op +BenchmarkGoji_Param5 1000000 1310 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Param5 314694 3803 ns/op 1392 B/op 11 allocs/op +BenchmarkGoJsonRest_Param5 308203 4108 ns/op 1097 B/op 16 allocs/op +BenchmarkGoRestful_Param5 115048 9787 ns/op 4288 B/op 14 allocs/op +BenchmarkGorillaMux_Param5 180812 5658 ns/op 1344 B/op 10 allocs/op +BenchmarkGowwwRouter_Param5 1000000 1156 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Param5 2395767 502 ns/op 160 B/op 1 allocs/op +BenchmarkHttpTreeMux_Param5 899263 2096 ns/op 576 B/op 6 allocs/op +BenchmarkKocha_Param5 1000000 1639 ns/op 440 B/op 10 allocs/op +BenchmarkLARS_Param5 5807994 203 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param5 272967 4087 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_Param5 120735 8886 ns/op 1232 B/op 11 allocs/op +BenchmarkPat_Param5 294714 4943 ns/op 888 B/op 29 allocs/op +BenchmarkPossum_Param5 1000000 1689 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Param5 1000000 1319 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Param5 1347289 883 ns/op 240 B/op 1 allocs/op +BenchmarkTango_Param5 617077 2091 ns/op 360 B/op 8 allocs/op +BenchmarkTigerTonic_Param5 113659 11212 ns/op 2279 B/op 39 allocs/op +BenchmarkTraffic_Param5 134148 9039 ns/op 2208 B/op 27 allocs/op +BenchmarkVulcan_Param5 1000000 1095 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_Param20 1000000 1838 ns/op 640 B/op 1 allocs/op +BenchmarkAero_Param20 17120668 66.1 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Param20 205585 5332 ns/op 1665 B/op 5 allocs/op +BenchmarkBeego_Param20 230522 5382 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Param20 167190 8076 ns/op 2031 B/op 6 allocs/op +BenchmarkChi_Param20 480528 3044 ns/op 432 B/op 3 allocs/op +BenchmarkCloudyKitRouter_Param20 1347794 872 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_Param20 1000000 1867 ns/op 640 B/op 1 allocs/op +BenchmarkEcho_Param20 1363526 897 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Param20 1607217 748 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param20 97314 11671 ns/op 3795 B/op 15 allocs/op +BenchmarkGoji_Param20 289407 4220 ns/op 1246 B/op 2 allocs/op +BenchmarkGojiv2_Param20 245186 4869 ns/op 1632 B/op 11 allocs/op +BenchmarkGoJsonRest_Param20 78049 15725 ns/op 4485 B/op 20 allocs/op +BenchmarkGoRestful_Param20 66907 18031 ns/op 6716 B/op 18 allocs/op +BenchmarkGorillaMux_Param20 81866 12422 ns/op 3452 B/op 12 allocs/op +BenchmarkGowwwRouter_Param20 955983 1688 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Param20 1000000 1629 ns/op 640 B/op 1 allocs/op +BenchmarkHttpTreeMux_Param20 108940 10241 ns/op 3195 B/op 10 allocs/op +BenchmarkKocha_Param20 197022 5488 ns/op 1808 B/op 27 allocs/op +BenchmarkLARS_Param20 2451241 490 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param20 106770 10788 ns/op 2923 B/op 12 allocs/op +BenchmarkMartini_Param20 69028 17112 ns/op 3596 B/op 13 allocs/op +BenchmarkPat_Param20 56275 21535 ns/op 4424 B/op 93 allocs/op +BenchmarkPossum_Param20 1000000 1705 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Param20 172215 7099 ns/op 2283 B/op 7 allocs/op +BenchmarkRivet_Param20 447265 2987 ns/op 1024 B/op 1 allocs/op +BenchmarkTango_Param20 327494 3850 ns/op 856 B/op 8 allocs/op +BenchmarkTigerTonic_Param20 27176 44571 ns/op 9871 B/op 119 allocs/op +BenchmarkTraffic_Param20 38828 31025 ns/op 7856 B/op 47 allocs/op +BenchmarkVulcan_Param20 560442 1807 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_ParamWrite 2712150 442 ns/op 40 B/op 2 allocs/op +BenchmarkAero_ParamWrite 6392880 189 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParamWrite 1000000 1338 ns/op 456 B/op 5 allocs/op +BenchmarkBeego_ParamWrite 821431 1886 ns/op 360 B/op 4 allocs/op +BenchmarkBone_ParamWrite 913227 2350 ns/op 816 B/op 6 allocs/op +BenchmarkChi_ParamWrite 1000000 1427 ns/op 432 B/op 3 allocs/op +BenchmarkCloudyKitRouter_ParamWrite 18645724 60.9 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_ParamWrite 4394764 264 ns/op 32 B/op 1 allocs/op +BenchmarkEcho_ParamWrite 5288883 242 ns/op 8 B/op 1 allocs/op +BenchmarkGin_ParamWrite 4584932 253 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParamWrite 866242 2094 ns/op 656 B/op 9 allocs/op +BenchmarkGoji_ParamWrite 1201875 1004 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_ParamWrite 317766 3777 ns/op 1360 B/op 13 allocs/op +BenchmarkGoJsonRest_ParamWrite 380242 3447 ns/op 1128 B/op 18 allocs/op +BenchmarkGoRestful_ParamWrite 131046 9340 ns/op 4200 B/op 15 allocs/op +BenchmarkGorillaMux_ParamWrite 298428 3970 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_ParamWrite 655940 2744 ns/op 976 B/op 8 allocs/op +BenchmarkHttpRouter_ParamWrite 5237014 219 ns/op 32 B/op 1 allocs/op +BenchmarkHttpTreeMux_ParamWrite 1379904 853 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_ParamWrite 2939042 400 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_ParamWrite 6181642 199 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParamWrite 352497 4670 ns/op 1176 B/op 14 allocs/op +BenchmarkMartini_ParamWrite 138259 8543 ns/op 1176 B/op 14 allocs/op +BenchmarkPat_ParamWrite 552386 3262 ns/op 960 B/op 15 allocs/op +BenchmarkPossum_ParamWrite 1000000 1711 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_ParamWrite 1000000 1085 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_ParamWrite 2374513 489 ns/op 112 B/op 2 allocs/op +BenchmarkTango_ParamWrite 1443907 812 ns/op 136 B/op 4 allocs/op +BenchmarkTigerTonic_ParamWrite 324264 4874 ns/op 1216 B/op 21 allocs/op +BenchmarkTraffic_ParamWrite 170726 7155 ns/op 2280 B/op 25 allocs/op +BenchmarkVulcan_ParamWrite 1498888 776 ns/op 98 B/op 3 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 +BenchmarkGin_GithubStatic 5866748 194 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 +BenchmarkAce_GithubStatic 5815826 205 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GithubStatic 10822906 106 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubStatic 1678065 707 ns/op 120 B/op 3 allocs/op +BenchmarkBeego_GithubStatic 828814 1717 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GithubStatic 67484 18858 ns/op 2880 B/op 60 allocs/op +BenchmarkCloudyKitRouter_GithubStatic 10219297 115 ns/op 0 B/op 0 allocs/op +BenchmarkChi_GithubStatic 1000000 1348 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GithubStatic 15220622 75.4 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_GithubStatic 7255897 158 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubStatic 1000000 1198 ns/op 296 B/op 5 allocs/op +BenchmarkGoji_GithubStatic 3659361 320 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_GithubStatic 402402 3384 ns/op 1312 B/op 10 allocs/op +BenchmarkGoRestful_GithubStatic 54592 22045 ns/op 4256 B/op 13 allocs/op +BenchmarkGoJsonRest_GithubStatic 801067 1673 ns/op 329 B/op 11 allocs/op +BenchmarkGorillaMux_GithubStatic 169690 8171 ns/op 976 B/op 9 allocs/op +BenchmarkGowwwRouter_GithubStatic 5372910 218 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_GithubStatic 10965576 103 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GithubStatic 10505365 106 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_GithubStatic 14153763 81.9 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_GithubStatic 7874017 152 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubStatic 696940 2678 ns/op 736 B/op 8 allocs/op +BenchmarkMartini_GithubStatic 102384 12276 ns/op 768 B/op 9 allocs/op +BenchmarkPat_GithubStatic 69907 17437 ns/op 3648 B/op 76 allocs/op +BenchmarkPossum_GithubStatic 1000000 1262 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_GithubStatic 1981592 614 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_GithubStatic 6103872 196 ns/op 0 B/op 0 allocs/op +BenchmarkTango_GithubStatic 629551 2023 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_GithubStatic 2801569 424 ns/op 48 B/op 1 allocs/op +BenchmarkTraffic_GithubStatic 63716 18009 ns/op 4664 B/op 90 allocs/op +BenchmarkVulcan_GithubStatic 885640 1177 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_GithubParam 2016942 582 ns/op 96 B/op 1 allocs/op +BenchmarkAero_GithubParam 4009522 296 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubParam 1000000 1575 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_GithubParam 796662 2038 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GithubParam 114823 10325 ns/op 1888 B/op 19 allocs/op +BenchmarkChi_GithubParam 1000000 1783 ns/op 432 B/op 3 allocs/op +BenchmarkCloudyKitRouter_GithubParam 3910996 303 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_GithubParam 2298172 521 ns/op 128 B/op 1 allocs/op +BenchmarkEcho_GithubParam 3336364 357 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GithubParam 2729161 439 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubParam 825784 2338 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_GithubParam 933397 1559 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GithubParam 253884 4335 ns/op 1408 B/op 13 allocs/op +BenchmarkGoJsonRest_GithubParam 575532 2967 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_GithubParam 38160 30638 ns/op 4352 B/op 16 allocs/op +BenchmarkGorillaMux_GithubParam 94554 12035 ns/op 1296 B/op 10 allocs/op +BenchmarkGowwwRouter_GithubParam 1000000 1223 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_GithubParam 2562079 468 ns/op 96 B/op 1 allocs/op +BenchmarkHttpTreeMux_GithubParam 1000000 1386 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_GithubParam 1573026 754 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_GithubParam 4203394 282 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubParam 365078 4137 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_GithubParam 71608 15811 ns/op 1152 B/op 11 allocs/op +BenchmarkPat_GithubParam 92768 13297 ns/op 2408 B/op 48 allocs/op +BenchmarkPossum_GithubParam 1000000 1704 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_GithubParam 1000000 1120 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GithubParam 1642794 720 ns/op 96 B/op 1 allocs/op +BenchmarkTango_GithubParam 574195 2345 ns/op 344 B/op 8 allocs/op +BenchmarkTigerTonic_GithubParam 272430 5493 ns/op 1176 B/op 22 allocs/op +BenchmarkTraffic_GithubParam 81914 15078 ns/op 2816 B/op 40 allocs/op +BenchmarkVulcan_GithubParam 581272 1902 ns/op 98 B/op 3 allocs/op + + +BenchmarkAce_GithubAll 10000 103571 ns/op 13792 B/op 167 allocs/op +BenchmarkAero_GithubAll 21366 55615 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubAll 5288 327648 ns/op 86448 B/op 943 allocs/op +BenchmarkBeego_GithubAll 3974 413453 ns/op 71456 B/op 609 allocs/op +BenchmarkBone_GithubAll 267 4450294 ns/op 720160 B/op 8620 allocs/op +BenchmarkChi_GithubAll 5067 358773 ns/op 87696 B/op 609 allocs/op +BenchmarkCloudyKitRouter_GithubAll 24210 49233 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_GithubAll 12508 95341 ns/op 20224 B/op 167 allocs/op +BenchmarkEcho_GithubAll 16353 73267 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GithubAll 15516 77716 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubAll 2908 466970 ns/op 131656 B/op 1686 allocs/op +BenchmarkGoji_GithubAll 1746 691392 ns/op 56112 B/op 334 allocs/op +BenchmarkGojiv2_GithubAll 954 1289604 ns/op 352720 B/op 4321 allocs/op +BenchmarkGoJsonRest_GithubAll 2013 599088 ns/op 134371 B/op 2737 allocs/op +BenchmarkGoRestful_GithubAll 223 5404307 ns/op 910144 B/op 2938 allocs/op +BenchmarkGorillaMux_GithubAll 202 5943565 ns/op 251650 B/op 1994 allocs/op +BenchmarkGowwwRouter_GithubAll 9009 227799 ns/op 72144 B/op 501 allocs/op +BenchmarkHttpRouter_GithubAll 14570 78718 ns/op 13792 B/op 167 allocs/op +BenchmarkHttpTreeMux_GithubAll 7226 242491 ns/op 65856 B/op 671 allocs/op +BenchmarkKocha_GithubAll 8282 159873 ns/op 23304 B/op 843 allocs/op +BenchmarkLARS_GithubAll 22711 52745 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubAll 2067 563117 ns/op 149409 B/op 1624 allocs/op +BenchmarkMartini_GithubAll 218 5455290 ns/op 226552 B/op 2325 allocs/op +BenchmarkPat_GithubAll 174 6801582 ns/op 1483152 B/op 26963 allocs/op +BenchmarkPossum_GithubAll 8113 263665 ns/op 84448 B/op 609 allocs/op +BenchmarkR2router_GithubAll 7172 247198 ns/op 77328 B/op 979 allocs/op +BenchmarkRivet_GithubAll 10000 128086 ns/op 16272 B/op 167 allocs/op +BenchmarkTango_GithubAll 3316 472753 ns/op 63825 B/op 1618 allocs/op +BenchmarkTigerTonic_GithubAll 1176 1041991 ns/op 193856 B/op 4474 allocs/op +BenchmarkTraffic_GithubAll 226 5312082 ns/op 820742 B/op 14114 allocs/op +BenchmarkVulcan_GithubAll 3904 304440 ns/op 19894 B/op 609 allocs/op ``` ## Google+ ``` -BenchmarkGin_GPlusStatic 10000000 183 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlusStatic 9172405 124 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 +BenchmarkAce_GPlusStatic 7784710 152 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GPlusStatic 12771894 89.2 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusStatic 2351325 512 ns/op 104 B/op 3 allocs/op +BenchmarkBeego_GPlusStatic 1000000 1643 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GPlusStatic 4419217 263 ns/op 32 B/op 1 allocs/op +BenchmarkChi_GPlusStatic 1000000 1282 ns/op 432 B/op 3 allocs/op +BenchmarkCloudyKitRouter_GPlusStatic 17730754 61.9 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_GPlusStatic 29549895 38.3 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_GPlusStatic 10521789 111 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusStatic 1000000 1053 ns/op 280 B/op 5 allocs/op +BenchmarkGoji_GPlusStatic 5209968 228 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_GPlusStatic 306363 3348 ns/op 1312 B/op 10 allocs/op +BenchmarkGoJsonRest_GPlusStatic 1000000 1424 ns/op 329 B/op 11 allocs/op +BenchmarkGoRestful_GPlusStatic 130754 8760 ns/op 3872 B/op 13 allocs/op +BenchmarkGorillaMux_GPlusStatic 496250 2860 ns/op 976 B/op 9 allocs/op +BenchmarkGowwwRouter_GPlusStatic 16401519 66.5 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_GPlusStatic 21323139 50.3 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlusStatic 14877926 68.7 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_GPlusStatic 18375128 57.6 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_GPlusStatic 11153810 101 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusStatic 652598 2720 ns/op 736 B/op 8 allocs/op +BenchmarkMartini_GPlusStatic 218824 6532 ns/op 768 B/op 9 allocs/op +BenchmarkPat_GPlusStatic 2825560 428 ns/op 96 B/op 2 allocs/op +BenchmarkPossum_GPlusStatic 1000000 1236 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_GPlusStatic 2222193 541 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_GPlusStatic 9802023 114 ns/op 0 B/op 0 allocs/op +BenchmarkTango_GPlusStatic 980658 1465 ns/op 200 B/op 8 allocs/op +BenchmarkTigerTonic_GPlusStatic 4882701 239 ns/op 32 B/op 1 allocs/op +BenchmarkTraffic_GPlusStatic 508060 3465 ns/op 1112 B/op 16 allocs/op +BenchmarkVulcan_GPlusStatic 1608979 725 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_GPlusParam 2962957 414 ns/op 64 B/op 1 allocs/op +BenchmarkAero_GPlusParam 5667668 202 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusParam 1000000 1271 ns/op 480 B/op 5 allocs/op +BenchmarkBeego_GPlusParam 869858 1874 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GPlusParam 869476 2395 ns/op 816 B/op 6 allocs/op +BenchmarkChi_GPlusParam 1000000 1469 ns/op 432 B/op 3 allocs/op +BenchmarkCloudyKitRouter_GPlusParam 11149783 108 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_GPlusParam 4007298 301 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_GPlusParam 6448201 174 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlusParam 5470827 218 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusParam 1000000 1939 ns/op 648 B/op 8 allocs/op +BenchmarkGoji_GPlusParam 1207621 997 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GPlusParam 271326 4013 ns/op 1328 B/op 11 allocs/op +BenchmarkGoJsonRest_GPlusParam 781062 2303 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_GPlusParam 121267 9871 ns/op 4192 B/op 14 allocs/op +BenchmarkGorillaMux_GPlusParam 228406 5156 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_GPlusParam 1000000 1074 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_GPlusParam 4399740 276 ns/op 64 B/op 1 allocs/op +BenchmarkHttpTreeMux_GPlusParam 1309540 898 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_GPlusParam 2930965 403 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_GPlusParam 7588237 151 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusParam 434997 4195 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_GPlusParam 148207 8144 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_GPlusParam 566829 2533 ns/op 576 B/op 11 allocs/op +BenchmarkPossum_GPlusParam 1000000 1723 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_GPlusParam 1000000 1100 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GPlusParam 3309052 331 ns/op 48 B/op 1 allocs/op +BenchmarkTango_GPlusParam 693728 1825 ns/op 264 B/op 8 allocs/op +BenchmarkTigerTonic_GPlusParam 417693 3800 ns/op 856 B/op 16 allocs/op +BenchmarkTraffic_GPlusParam 179424 6641 ns/op 1872 B/op 21 allocs/op +BenchmarkVulcan_GPlusParam 1000000 1063 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_GPlus2Params 2720149 460 ns/op 64 B/op 1 allocs/op +BenchmarkAero_GPlus2Params 3525165 343 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlus2Params 1000000 1502 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_GPlus2Params 730123 2102 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GPlus2Params 253177 5583 ns/op 1168 B/op 10 allocs/op +BenchmarkChi_GPlus2Params 1000000 1531 ns/op 432 B/op 3 allocs/op +BenchmarkCloudyKitRouter_GPlus2Params 6943176 168 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_GPlus2Params 2912601 413 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_GPlus2Params 4149189 278 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlus2Params 3271269 356 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlus2Params 915531 2321 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_GPlus2Params 1000000 1413 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GPlus2Params 256640 4521 ns/op 1408 B/op 14 allocs/op +BenchmarkGoJsonRest_GPlus2Params 499140 3076 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_GPlus2Params 105928 10148 ns/op 4384 B/op 16 allocs/op +BenchmarkGorillaMux_GPlus2Params 110953 9682 ns/op 1296 B/op 10 allocs/op +BenchmarkGowwwRouter_GPlus2Params 1000000 1112 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_GPlus2Params 3491893 321 ns/op 64 B/op 1 allocs/op +BenchmarkHttpTreeMux_GPlus2Params 1000000 1341 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_GPlus2Params 1445288 790 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_GPlus2Params 6644953 185 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlus2Params 424291 4321 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_GPlus2Params 70866 16407 ns/op 1200 B/op 13 allocs/op +BenchmarkPat_GPlus2Params 121308 10221 ns/op 2168 B/op 33 allocs/op +BenchmarkPossum_GPlus2Params 1000000 1847 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_GPlus2Params 1000000 1267 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GPlus2Params 2017526 590 ns/op 96 B/op 1 allocs/op +BenchmarkTango_GPlus2Params 846003 2143 ns/op 344 B/op 8 allocs/op +BenchmarkTigerTonic_GPlus2Params 303597 5736 ns/op 1200 B/op 22 allocs/op +BenchmarkTraffic_GPlus2Params 95032 12817 ns/op 2248 B/op 28 allocs/op +BenchmarkVulcan_GPlus2Params 692610 1575 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_GPlusAll 271720 4948 ns/op 640 B/op 11 allocs/op +BenchmarkAero_GPlusAll 367956 2926 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusAll 68161 17883 ns/op 5488 B/op 61 allocs/op +BenchmarkBeego_GPlusAll 46634 25369 ns/op 4576 B/op 39 allocs/op +BenchmarkBone_GPlusAll 24628 49198 ns/op 11744 B/op 109 allocs/op +BenchmarkChi_GPlusAll 60778 19356 ns/op 5616 B/op 39 allocs/op +BenchmarkCloudyKitRouter_GPlusAll 706952 1693 ns/op 0 B/op 0 allocs/op +BenchmarkDenco_GPlusAll 327422 4222 ns/op 672 B/op 11 allocs/op +BenchmarkEcho_GPlusAll 331987 3176 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlusAll 289526 3559 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusAll 45805 26768 ns/op 8040 B/op 103 allocs/op +BenchmarkGoji_GPlusAll 74786 14428 ns/op 3696 B/op 22 allocs/op +BenchmarkGojiv2_GPlusAll 23822 50355 ns/op 17616 B/op 154 allocs/op +BenchmarkGoJsonRest_GPlusAll 35280 32989 ns/op 8117 B/op 170 allocs/op +BenchmarkGoRestful_GPlusAll 10000 129418 ns/op 55520 B/op 192 allocs/op +BenchmarkGorillaMux_GPlusAll 15968 76492 ns/op 16112 B/op 128 allocs/op +BenchmarkGowwwRouter_GPlusAll 100096 12644 ns/op 4752 B/op 33 allocs/op +BenchmarkHttpRouter_GPlusAll 474584 3704 ns/op 640 B/op 11 allocs/op +BenchmarkHttpTreeMux_GPlusAll 98506 12480 ns/op 4032 B/op 38 allocs/op +BenchmarkKocha_GPlusAll 213709 7358 ns/op 976 B/op 43 allocs/op +BenchmarkLARS_GPlusAll 466608 2363 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusAll 34136 35790 ns/op 9568 B/op 104 allocs/op +BenchmarkMartini_GPlusAll 8911 124543 ns/op 14016 B/op 145 allocs/op +BenchmarkPat_GPlusAll 17391 69198 ns/op 15264 B/op 271 allocs/op +BenchmarkPossum_GPlusAll 66774 17004 ns/op 5408 B/op 39 allocs/op +BenchmarkR2router_GPlusAll 79681 13996 ns/op 5040 B/op 63 allocs/op +BenchmarkRivet_GPlusAll 258788 5344 ns/op 768 B/op 11 allocs/op +BenchmarkTango_GPlusAll 46930 25591 ns/op 3656 B/op 104 allocs/op +BenchmarkTigerTonic_GPlusAll 20768 58038 ns/op 11600 B/op 242 allocs/op +BenchmarkTraffic_GPlusAll 10000 108031 ns/op 26248 B/op 341 allocs/op +BenchmarkVulcan_GPlusAll 71826 15724 ns/op 1274 B/op 39 allocs/op ``` ## Parse.com ``` -BenchmarkGin_ParseStatic 10000000 133 ns/op 0 B/op 0 allocs/op +BenchmarkGin_ParseStatic 8683893 140 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 +BenchmarkAce_ParseStatic 7255582 160 ns/op 0 B/op 0 allocs/op +BenchmarkAero_ParseStatic 11960128 95.0 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseStatic 1791033 659 ns/op 120 B/op 3 allocs/op +BenchmarkBeego_ParseStatic 937918 1688 ns/op 352 B/op 3 allocs/op +BenchmarkBone_ParseStatic 1261682 949 ns/op 144 B/op 3 allocs/op +BenchmarkChi_ParseStatic 1000000 1303 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_ParseStatic 23731242 49.8 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_ParseStatic 10585060 116 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseStatic 1000000 1156 ns/op 296 B/op 5 allocs/op +BenchmarkGoji_ParseStatic 3927530 300 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_ParseStatic 474836 3281 ns/op 1312 B/op 10 allocs/op +BenchmarkGoJsonRest_ParseStatic 1000000 1445 ns/op 329 B/op 11 allocs/op +BenchmarkGoRestful_ParseStatic 101262 11612 ns/op 4256 B/op 13 allocs/op +BenchmarkGorillaMux_ParseStatic 562705 3530 ns/op 976 B/op 9 allocs/op +BenchmarkGowwwRouter_ParseStatic 16479007 69.5 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_ParseStatic 23205590 51.5 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParseStatic 10763127 106 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_ParseStatic 17850259 60.9 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_ParseStatic 10727432 108 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseStatic 685586 2665 ns/op 736 B/op 8 allocs/op +BenchmarkMartini_ParseStatic 200642 7158 ns/op 768 B/op 9 allocs/op +BenchmarkPat_ParseStatic 1000000 1139 ns/op 240 B/op 5 allocs/op +BenchmarkPossum_ParseStatic 1000000 1241 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_ParseStatic 2035426 597 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_ParseStatic 9707011 127 ns/op 0 B/op 0 allocs/op +BenchmarkTango_ParseStatic 910617 1693 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_ParseStatic 3168885 385 ns/op 48 B/op 1 allocs/op +BenchmarkTraffic_ParseStatic 493339 4264 ns/op 1256 B/op 19 allocs/op +BenchmarkVulcan_ParseStatic 1394142 848 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_ParseParam 3106903 387 ns/op 64 B/op 1 allocs/op +BenchmarkAero_ParseParam 8045266 141 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseParam 1000000 1434 ns/op 467 B/op 5 allocs/op +BenchmarkBeego_ParseParam 951460 1937 ns/op 352 B/op 3 allocs/op +BenchmarkBone_ParseParam 855555 2776 ns/op 896 B/op 7 allocs/op +BenchmarkChi_ParseParam 1000000 1457 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_ParseParam 4084116 301 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_ParseParam 8440170 142 ns/op 0 B/op 0 allocs/op +BenchmarkGin_ParseParam 7716948 157 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseParam 886284 2045 ns/op 664 B/op 8 allocs/op +BenchmarkGoji_ParseParam 1000000 1167 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_ParseParam 269731 3945 ns/op 1360 B/op 12 allocs/op +BenchmarkGoJsonRest_ParseParam 719587 2277 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_ParseParam 96408 11925 ns/op 4576 B/op 14 allocs/op +BenchmarkGorillaMux_ParseParam 289303 4154 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_ParseParam 1000000 1070 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_ParseParam 4917758 232 ns/op 64 B/op 1 allocs/op +BenchmarkHttpTreeMux_ParseParam 1445443 828 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_ParseParam 3116233 382 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_ParseParam 10584750 113 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseParam 413617 3872 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_ParseParam 166545 7605 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_ParseParam 491829 3394 ns/op 992 B/op 15 allocs/op +BenchmarkPossum_ParseParam 1000000 1692 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_ParseParam 1000000 1059 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_ParseParam 3572359 311 ns/op 48 B/op 1 allocs/op +BenchmarkTango_ParseParam 787552 1889 ns/op 280 B/op 8 allocs/op +BenchmarkTigerTonic_ParseParam 487208 3706 ns/op 784 B/op 15 allocs/op +BenchmarkTraffic_ParseParam 186190 5812 ns/op 1896 B/op 21 allocs/op +BenchmarkVulcan_ParseParam 1275432 892 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_Parse2Params 2959621 412 ns/op 64 B/op 1 allocs/op +BenchmarkAero_Parse2Params 6208641 192 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Parse2Params 1000000 1512 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_Parse2Params 761940 1973 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Parse2Params 715987 2582 ns/op 848 B/op 6 allocs/op +BenchmarkChi_Parse2Params 1000000 1495 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_Parse2Params 3585452 341 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_Parse2Params 5193693 204 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Parse2Params 5338316 236 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Parse2Params 939637 2299 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_Parse2Params 1000000 1094 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Parse2Params 339514 3733 ns/op 1344 B/op 11 allocs/op +BenchmarkGoJsonRest_Parse2Params 512572 2733 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_Parse2Params 95913 12973 ns/op 4928 B/op 14 allocs/op +BenchmarkGorillaMux_Parse2Params 261208 4758 ns/op 1296 B/op 10 allocs/op +BenchmarkGowwwRouter_Parse2Params 1000000 1084 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Parse2Params 4399953 277 ns/op 64 B/op 1 allocs/op +BenchmarkHttpTreeMux_Parse2Params 1000000 1198 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_Parse2Params 1669431 683 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_Parse2Params 8535754 142 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Parse2Params 424590 3959 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_Parse2Params 162448 8141 ns/op 1152 B/op 11 allocs/op +BenchmarkPat_Parse2Params 431336 3484 ns/op 752 B/op 16 allocs/op +BenchmarkPossum_Parse2Params 1000000 1721 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Parse2Params 1000000 1136 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Parse2Params 2630935 442 ns/op 96 B/op 1 allocs/op +BenchmarkTango_Parse2Params 759218 1876 ns/op 312 B/op 8 allocs/op +BenchmarkTigerTonic_Parse2Params 290810 5558 ns/op 1168 B/op 22 allocs/op +BenchmarkTraffic_Parse2Params 181099 6917 ns/op 1944 B/op 22 allocs/op +BenchmarkVulcan_Parse2Params 1000000 1080 ns/op 98 B/op 3 allocs/op + +BenchmarkAce_ParseAll 162906 7888 ns/op 640 B/op 16 allocs/op +BenchmarkAero_ParseAll 219260 4833 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseAll 37566 32863 ns/op 8928 B/op 110 allocs/op +BenchmarkBeego_ParseAll 25400 46518 ns/op 9152 B/op 78 allocs/op +BenchmarkBone_ParseAll 19568 61814 ns/op 16208 B/op 147 allocs/op +BenchmarkChi_ParseAll 30562 38281 ns/op 11232 B/op 78 allocs/op +BenchmarkDenco_ParseAll 232554 6371 ns/op 928 B/op 16 allocs/op +BenchmarkEcho_ParseAll 224400 5090 ns/op 0 B/op 0 allocs/op +BenchmarkGin_ParseAll 189829 6134 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseAll 25446 47000 ns/op 13728 B/op 181 allocs/op +BenchmarkGoji_ParseAll 50503 22949 ns/op 5376 B/op 32 allocs/op +BenchmarkGojiv2_ParseAll 12806 93106 ns/op 34448 B/op 277 allocs/op +BenchmarkGoJsonRest_ParseAll 20764 57021 ns/op 13866 B/op 321 allocs/op +BenchmarkGoRestful_ParseAll 4234 317238 ns/op 117600 B/op 354 allocs/op +BenchmarkGorillaMux_ParseAll 10000 146942 ns/op 30288 B/op 250 allocs/op +BenchmarkGowwwRouter_ParseAll 62548 19363 ns/op 6912 B/op 48 allocs/op +BenchmarkHttpRouter_ParseAll 286662 5091 ns/op 640 B/op 16 allocs/op +BenchmarkHttpTreeMux_ParseAll 66952 18262 ns/op 5728 B/op 51 allocs/op +BenchmarkKocha_ParseAll 109771 9811 ns/op 1112 B/op 54 allocs/op +BenchmarkLARS_ParseAll 272516 3976 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseAll 17094 71634 ns/op 19136 B/op 208 allocs/op +BenchmarkMartini_ParseAll 6799 208122 ns/op 25072 B/op 253 allocs/op +BenchmarkPat_ParseAll 15993 74594 ns/op 15216 B/op 308 allocs/op +BenchmarkPossum_ParseAll 34897 33398 ns/op 10816 B/op 78 allocs/op +BenchmarkR2router_ParseAll 46909 25410 ns/op 8352 B/op 120 allocs/op +BenchmarkRivet_ParseAll 185193 7725 ns/op 912 B/op 16 allocs/op +BenchmarkTango_ParseAll 24481 47963 ns/op 7168 B/op 208 allocs/op +BenchmarkTigerTonic_ParseAll 15236 79623 ns/op 16048 B/op 332 allocs/op +BenchmarkTraffic_ParseAll 8955 169411 ns/op 45520 B/op 605 allocs/op +BenchmarkVulcan_ParseAll 40406 28971 ns/op 2548 B/op 78 allocs/op ``` From 3abc96e3cdc4e74daf52fff5fe7eb36b9674904b Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 1 Dec 2019 19:53:03 +0800 Subject: [PATCH 31/90] tree: sync part httprouter codes and reduce if/else (#2163) --- tree.go | 370 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 188 insertions(+), 182 deletions(-) diff --git a/tree.go b/tree.go index 41947570..1187f3f6 100644 --- a/tree.go +++ b/tree.go @@ -62,6 +62,15 @@ func min(a, b int) int { return b } +func longestCommonPrefix(a, b string) int { + i := 0 + max := min(len(a), len(b)) + for i < max && a[i] == b[i] { + i++ + } + return i +} + func countParams(path string) uint8 { var n uint for i := 0; i < len(path); i++ { @@ -127,135 +136,132 @@ func (n *node) addRoute(path string, handlers HandlersChain) { n.priority++ numParams := countParams(path) + // Empty tree + if len(n.path) == 0 && len(n.children) == 0 { + n.insertChild(numParams, path, fullPath, handlers) + n.nType = root + return + } + parentFullPathIndex := 0 - // non-empty tree - if len(n.path) > 0 || len(n.children) > 0 { - walk: - for { - // Update maxParams of the current node - if numParams > n.maxParams { - n.maxParams = numParams +walk: + for { + // Update maxParams of the current node + if numParams > n.maxParams { + n.maxParams = numParams + } + + // Find the longest common prefix. + // This also implies that the common prefix contains no ':' or '*' + // since the existing key can't contain those chars. + i := longestCommonPrefix(path, n.path) + + // Split edge + if i < len(n.path) { + child := node{ + path: n.path[i:], + wildChild: n.wildChild, + indices: n.indices, + children: n.children, + handlers: n.handlers, + priority: n.priority - 1, + fullPath: n.fullPath, } - // Find the longest common prefix. - // This also implies that the common prefix contains no ':' or '*' - // since the existing key can't contain those chars. - i := 0 - max := min(len(path), len(n.path)) - for i < max && path[i] == n.path[i] { - i++ + // Update maxParams (max of all children) + for i := range child.children { + if child.children[i].maxParams > child.maxParams { + child.maxParams = child.children[i].maxParams + } } - // Split edge - if i < len(n.path) { - child := node{ - path: n.path[i:], - wildChild: n.wildChild, - indices: n.indices, - children: n.children, - handlers: n.handlers, - priority: n.priority - 1, - fullPath: n.fullPath, + n.children = []*node{&child} + // []byte for proper unicode char conversion, see #65 + n.indices = string([]byte{n.path[i]}) + n.path = path[:i] + n.handlers = nil + n.wildChild = false + n.fullPath = fullPath[:parentFullPathIndex+i] + } + + // Make new node a child of this node + if i < len(path) { + path = path[i:] + + if n.wildChild { + parentFullPathIndex += len(n.path) + n = n.children[0] + n.priority++ + + // Update maxParams of the child node + if numParams > n.maxParams { + n.maxParams = numParams } + numParams-- - // Update maxParams (max of all children) - for i := range child.children { - if child.children[i].maxParams > child.maxParams { - child.maxParams = child.children[i].maxParams - } - } - - n.children = []*node{&child} - // []byte for proper unicode char conversion, see #65 - n.indices = string([]byte{n.path[i]}) - n.path = path[:i] - n.handlers = nil - n.wildChild = false - n.fullPath = fullPath[:parentFullPathIndex+i] - } - - // Make new node a child of this node - if i < len(path) { - path = path[i:] - - if n.wildChild { - parentFullPathIndex += len(n.path) - n = n.children[0] - n.priority++ - - // Update maxParams of the child node - if numParams > n.maxParams { - n.maxParams = numParams - } - numParams-- - - // Check if the wildcard matches - if len(path) >= len(n.path) && n.path == path[:len(n.path)] { - // check for longer wildcard, e.g. :name and :names - if len(n.path) >= len(path) || path[len(n.path)] == '/' { - continue walk - } - } - - pathSeg := path - if n.nType != catchAll { - pathSeg = strings.SplitN(path, "/", 2)[0] - } - prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path - panic("'" + pathSeg + - "' in new path '" + fullPath + - "' conflicts with existing wildcard '" + n.path + - "' in existing prefix '" + prefix + - "'") - } - - c := path[0] - - // slash after param - if n.nType == param && c == '/' && len(n.children) == 1 { - parentFullPathIndex += len(n.path) - n = n.children[0] - n.priority++ - continue walk - } - - // Check if a child with the next path byte exists - for i := 0; i < len(n.indices); i++ { - if c == n.indices[i] { - parentFullPathIndex += len(n.path) - i = n.incrementChildPrio(i) - n = n.children[i] + // Check if the wildcard matches + if len(path) >= len(n.path) && n.path == path[:len(n.path)] { + // check for longer wildcard, e.g. :name and :names + if len(n.path) >= len(path) || path[len(n.path)] == '/' { continue walk } } - // Otherwise insert it - if c != ':' && c != '*' { - // []byte for proper unicode char conversion, see #65 - n.indices += string([]byte{c}) - child := &node{ - maxParams: numParams, - fullPath: fullPath, - } - n.children = append(n.children, child) - n.incrementChildPrio(len(n.indices) - 1) - n = child + pathSeg := path + if n.nType != catchAll { + pathSeg = strings.SplitN(path, "/", 2)[0] } - n.insertChild(numParams, path, fullPath, handlers) - return - - } else if i == len(path) { // Make node a (in-path) leaf - if n.handlers != nil { - panic("handlers are already registered for path '" + fullPath + "'") - } - n.handlers = handlers + prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path + panic("'" + pathSeg + + "' in new path '" + fullPath + + "' conflicts with existing wildcard '" + n.path + + "' in existing prefix '" + prefix + + "'") } + + c := path[0] + + // slash after param + if n.nType == param && c == '/' && len(n.children) == 1 { + parentFullPathIndex += len(n.path) + n = n.children[0] + n.priority++ + continue walk + } + + // Check if a child with the next path byte exists + for i := 0; i < len(n.indices); i++ { + if c == n.indices[i] { + parentFullPathIndex += len(n.path) + i = n.incrementChildPrio(i) + n = n.children[i] + continue walk + } + } + + // Otherwise insert it + if c != ':' && c != '*' { + // []byte for proper unicode char conversion, see #65 + n.indices += string([]byte{c}) + child := &node{ + maxParams: numParams, + fullPath: fullPath, + } + n.children = append(n.children, child) + n.incrementChildPrio(len(n.indices) - 1) + n = child + } + n.insertChild(numParams, path, fullPath, handlers) return + + } else if i == len(path) { // Make node a (in-path) leaf + if n.handlers != nil { + panic("handlers are already registered for path '" + fullPath + "'") + } + n.handlers = handlers } - } else { // Empty tree - n.insertChild(numParams, path, fullPath, handlers) - n.nType = root + return } } @@ -542,75 +548,7 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa path = path[len(n.path):] ciPath = append(ciPath, n.path...) - if len(path) > 0 { - // If this node does not have a wildcard (param or catchAll) child, - // we can just look up the next child node and continue to walk down - // the tree - if !n.wildChild { - r := unicode.ToLower(rune(path[0])) - for i, index := range n.indices { - // must use recursive approach since both index and - // ToLower(index) could exist. We must check both. - if r == unicode.ToLower(index) { - out, found := n.children[i].findCaseInsensitivePath(path, fixTrailingSlash) - if found { - return append(ciPath, out...), true - } - } - } - - // Nothing found. We can recommend to redirect to the same URL - // without a trailing slash if a leaf exists for that path - found = fixTrailingSlash && path == "/" && n.handlers != nil - return - } - - n = n.children[0] - switch n.nType { - case param: - // find param end (either '/' or path end) - k := 0 - for k < len(path) && path[k] != '/' { - k++ - } - - // add param value to case insensitive path - ciPath = append(ciPath, path[:k]...) - - // we need to go deeper! - if k < len(path) { - if len(n.children) > 0 { - path = path[k:] - n = n.children[0] - continue - } - - // ... but we can't - if fixTrailingSlash && len(path) == k+1 { - return ciPath, true - } - return - } - - if n.handlers != nil { - return ciPath, true - } else if fixTrailingSlash && len(n.children) == 1 { - // No handle found. Check if a handle for this path + a - // trailing slash exists - n = n.children[0] - if n.path == "/" && n.handlers != nil { - return append(ciPath, '/'), true - } - } - return - - case catchAll: - return append(ciPath, path...), true - - default: - panic("invalid node type") - } - } else { + if len(path) == 0 { // We should have reached the node containing the handle. // Check if this node has a handle registered. if n.handlers != nil { @@ -633,6 +571,74 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa } return } + + // If this node does not have a wildcard (param or catchAll) child, + // we can just look up the next child node and continue to walk down + // the tree + if !n.wildChild { + r := unicode.ToLower(rune(path[0])) + for i, index := range n.indices { + // must use recursive approach since both index and + // ToLower(index) could exist. We must check both. + if r == unicode.ToLower(index) { + out, found := n.children[i].findCaseInsensitivePath(path, fixTrailingSlash) + if found { + return append(ciPath, out...), true + } + } + } + + // Nothing found. We can recommend to redirect to the same URL + // without a trailing slash if a leaf exists for that path + found = fixTrailingSlash && path == "/" && n.handlers != nil + return + } + + n = n.children[0] + switch n.nType { + case param: + // find param end (either '/' or path end) + k := 0 + for k < len(path) && path[k] != '/' { + k++ + } + + // add param value to case insensitive path + ciPath = append(ciPath, path[:k]...) + + // we need to go deeper! + if k < len(path) { + if len(n.children) > 0 { + path = path[k:] + n = n.children[0] + continue + } + + // ... but we can't + if fixTrailingSlash && len(path) == k+1 { + return ciPath, true + } + return + } + + if n.handlers != nil { + return ciPath, true + } else if fixTrailingSlash && len(n.children) == 1 { + // No handle found. Check if a handle for this path + a + // trailing slash exists + n = n.children[0] + if n.path == "/" && n.handlers != nil { + return append(ciPath, '/'), true + } + } + return + + case catchAll: + return append(ciPath, path...), true + + default: + panic("invalid node type") + } } // Nothing found. From 77b83441698ffcc400d02b9849f15d7a47e0b8bc Mon Sep 17 00:00:00 2001 From: Victor Castell Date: Mon, 2 Dec 2019 13:59:56 +0100 Subject: [PATCH 32/90] Add project to README (#2165) Add Dkron as user of Gin in the README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 012152a0..f8bc4239 100644 --- a/README.md +++ b/README.md @@ -2096,3 +2096,4 @@ Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framewor * [krakend](https://github.com/devopsfaith/krakend): Ultra performant API Gateway with middlewares. * [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. * [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes. +* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. From 7c21e04f628f1cabdf4029fd5c78a8f2b152faae Mon Sep 17 00:00:00 2001 From: thinkerou Date: Wed, 4 Dec 2019 07:56:01 +0800 Subject: [PATCH 33/90] fix maxParams bug (#2166) --- tree.go | 4 ++++ tree_test.go | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/tree.go b/tree.go index 1187f3f6..b46ec828 100644 --- a/tree.go +++ b/tree.go @@ -357,6 +357,10 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle maxParams: 1, fullPath: fullPath, } + // update maxParams of the parent node + if n.maxParams < 1 { + n.maxParams = 1 + } n.children = []*node{child} n.indices = string(path[i]) n = child diff --git a/tree_test.go b/tree_test.go index e6e28865..0fe2fe00 100644 --- a/tree_test.go +++ b/tree_test.go @@ -368,6 +368,13 @@ func TestTreeCatchAllConflictRoot(t *testing.T) { testRoutes(t, routes) } +func TestTreeCatchMaxParams(t *testing.T) { + tree := &node{} + var route = "/cmd/*filepath" + tree.addRoute(route, fakeHandler(route)) + checkMaxParams(t, tree) +} + func TestTreeDoubleWildcard(t *testing.T) { const panicMsg = "only one wildcard per path segment is allowed" From c6544855d7244db15858cbc0bb200da5efa45b83 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 8 Dec 2019 18:35:08 +0800 Subject: [PATCH 34/90] tree: sync httprouter update (#2171) --- tree.go | 61 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/tree.go b/tree.go index b46ec828..ffd99896 100644 --- a/tree.go +++ b/tree.go @@ -107,16 +107,15 @@ type node struct { // 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 + cs := n.children + cs[pos].priority++ + prio := cs[pos].priority - // adjust position (move to front) + // Adjust position (move to front) newPos := pos - for newPos > 0 && n.children[newPos-1].priority < prio { - // swap node positions - n.children[newPos-1], n.children[newPos] = n.children[newPos], n.children[newPos-1] - - newPos-- + for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- { + // Swap node positions + cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1] } // build new index char string @@ -231,7 +230,7 @@ walk: } // Check if a child with the next path byte exists - for i := 0; i < len(n.indices); i++ { + for i, max := 0, len(n.indices); i < max; i++ { if c == n.indices[i] { parentFullPathIndex += len(n.path) i = n.incrementChildPrio(i) @@ -404,17 +403,20 @@ func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue) value.params = po walk: // Outer loop for walking the tree for { - if len(path) > len(n.path) { - if path[:len(n.path)] == n.path { - path = path[len(n.path):] + prefix := n.path + if len(path) > len(prefix) { + if path[:len(prefix)] == prefix { + path = path[len(prefix):] // If this node does not have a wildcard (param or catchAll) // child, we can just look up the next child node and continue // to walk down the tree if !n.wildChild { c := path[0] - for i := 0; i < len(n.indices); i++ { - if c == n.indices[i] { + indices := n.indices + for i, max := 0, len(indices); i < max; i++ { + if c == indices[i] { n = n.children[i] + prefix = n.path continue walk } } @@ -458,6 +460,7 @@ walk: // Outer loop for walking the tree if len(n.children) > 0 { path = path[end:] n = n.children[0] + prefix = n.path continue walk } @@ -504,7 +507,7 @@ walk: // Outer loop for walking the tree panic("invalid node type") } } - } else if path == n.path { + } else if path == prefix { // We should have reached the node containing the handle. // Check if this node has a handle registered. if value.handlers = n.handlers; value.handlers != nil { @@ -519,8 +522,9 @@ walk: // Outer loop for walking the tree // No handle found. Check if a handle for this path + a // trailing slash exists for trailing slash recommendation - for i := 0; i < len(n.indices); i++ { - if n.indices[i] == '/' { + indices := n.indices + for i, max := 0, len(indices); i < max; i++ { + if indices[i] == '/' { n = n.children[i] value.tsr = (len(n.path) == 1 && n.handlers != nil) || (n.nType == catchAll && n.children[0].handlers != nil) @@ -534,8 +538,8 @@ walk: // Outer loop for walking the tree // Nothing found. We can recommend to redirect to the same URL with an // extra trailing slash if a leaf exists for that path value.tsr = (path == "/") || - (len(n.path) == len(path)+1 && n.path[len(path)] == '/' && - path == n.path[:len(n.path)-1] && n.handlers != nil) + (len(prefix) == len(path)+1 && prefix[len(path)] == '/' && + path == prefix[:len(prefix)-1] && n.handlers != nil) return } } @@ -601,25 +605,25 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa n = n.children[0] switch n.nType { case param: - // find param end (either '/' or path end) - k := 0 - for k < len(path) && path[k] != '/' { - k++ + // Find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ } // add param value to case insensitive path - ciPath = append(ciPath, path[:k]...) + ciPath = append(ciPath, path[:end]...) // we need to go deeper! - if k < len(path) { + if end < len(path) { if len(n.children) > 0 { - path = path[k:] + path = path[end:] n = n.children[0] continue } // ... but we can't - if fixTrailingSlash && len(path) == k+1 { + if fixTrailingSlash && len(path) == end+1 { return ciPath, true } return @@ -627,7 +631,8 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa if n.handlers != nil { return ciPath, true - } else if fixTrailingSlash && len(n.children) == 1 { + } + if fixTrailingSlash && len(n.children) == 1 { // No handle found. Check if a handle for this path + a // trailing slash exists n = n.children[0] From 6e16da8683136c68164b9011fc5678f46ad78d27 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 8 Dec 2019 19:34:05 +0800 Subject: [PATCH 35/90] tree: sync httprouter update (#2172) --- tree.go | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/tree.go b/tree.go index ffd99896..3a8a4355 100644 --- a/tree.go +++ b/tree.go @@ -253,13 +253,13 @@ walk: } n.insertChild(numParams, path, fullPath, handlers) return - - } else if i == len(path) { // Make node a (in-path) leaf - if n.handlers != nil { - panic("handlers are already registered for path '" + fullPath + "'") - } - n.handlers = handlers } + + // Otherwise and handle to current node + if n.handlers != nil { + panic("handlers are already registered for path '" + fullPath + "'") + } + n.handlers = handlers return } } @@ -267,31 +267,31 @@ walk: func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) { var offset int // already handled bytes of the path - // find prefix until first wildcard (beginning with ':' or '*') + // Find prefix until first wildcard (beginning with ':' or '*') for i, max := 0, len(path); numParams > 0; i++ { c := path[i] if c != ':' && c != '*' { continue } - // find wildcard end (either '/' or path end) + // Find wildcard end (either '/' or path end) and check the name for invalid characters end := i + 1 - for end < max && path[end] != '/' { - switch path[end] { - // the wildcard name must not contain ':' and '*' - case ':', '*': - panic("only one wildcard per path segment is allowed, has: '" + - path[i:] + "' in path '" + fullPath + "'") - default: - end++ + invalid := false + for end < max { + c := path[end] + if c == '/' { + break } + if c == ':' || c == '*' { + invalid = true + } + end++ } - // check if this Node existing children which would be - // unreachable if we insert the wildcard here - if len(n.children) > 0 { - panic("wildcard route '" + path[i:end] + - "' conflicts with existing children in path '" + fullPath + "'") + // The wildcard name must not contain ':' and '*' + if invalid { + panic("only one wildcard per path segment is allowed, has: '" + + path[i:end] + "' in path '" + fullPath + "'") } // check if the wildcard has a name @@ -299,6 +299,13 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle panic("wildcards must be named with a non-empty name in path '" + fullPath + "'") } + // Check if this node has existing children which would be + // unreachable if we insert the wildcard here + if len(n.children) > 0 { + panic("wildcard route '" + path[i:end] + + "' conflicts with existing children in path '" + fullPath + "'") + } + if c == ':' { // param // split path at the beginning of the wildcard if i > 0 { From 168fa945168119b7f72d5359f4abaf311e52f1b8 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 9 Dec 2019 15:04:35 +0800 Subject: [PATCH 36/90] tree: sync httprouter update (#2173) --- tree.go | 164 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 89 insertions(+), 75 deletions(-) diff --git a/tree.go b/tree.go index 3a8a4355..b09d3f67 100644 --- a/tree.go +++ b/tree.go @@ -264,71 +264,80 @@ walk: } } -func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) { - var offset int // already handled bytes of the path - - // Find prefix until first wildcard (beginning with ':' or '*') - for i, max := 0, len(path); numParams > 0; i++ { - c := path[i] +// Search for a wildcard segment and check the name for invalid characters. +// Returns -1 as index, if no wildcard war found. +func findWildcard(path string) (wildcard string, i int, valid bool) { + // Find start + for start, c := range []byte(path) { + // A wildcard starts with ':' (param) or '*' (catch-all) if c != ':' && c != '*' { continue } - // Find wildcard end (either '/' or path end) and check the name for invalid characters - end := i + 1 - invalid := false - for end < max { - c := path[end] - if c == '/' { - break + // Find end and check for invalid characters + valid = true + for end, c := range []byte(path[start+1:]) { + switch c { + case '/': + return path[start : start+1+end], start, valid + case ':', '*': + valid = false } - if c == ':' || c == '*' { - invalid = true - } - end++ + } + return path[start:], start, valid + } + return "", -1, false +} + +func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) { + for numParams > 0 { + // Find prefix until first wildcard + wildcard, i, valid := findWildcard(path) + if i < 0 { // No wildcard found + break } // The wildcard name must not contain ':' and '*' - if invalid { + if !valid { panic("only one wildcard per path segment is allowed, has: '" + - path[i:end] + "' in path '" + fullPath + "'") + wildcard + "' in path '" + fullPath + "'") } // check if the wildcard has a name - if end-i < 2 { + if len(wildcard) < 2 { panic("wildcards must be named with a non-empty name in path '" + fullPath + "'") } // Check if this node has existing children which would be // unreachable if we insert the wildcard here if len(n.children) > 0 { - panic("wildcard route '" + path[i:end] + + panic("wildcard segment '" + wildcard + "' conflicts with existing children in path '" + fullPath + "'") } - if c == ':' { // param - // split path at the beginning of the wildcard + if wildcard[0] == ':' { // param if i > 0 { - n.path = path[offset:i] - offset = i + // Insert prefix before the current wildcard + n.path = path[:i] + path = path[i:] } + n.wildChild = true child := &node{ nType: param, + path: wildcard, maxParams: numParams, fullPath: fullPath, } n.children = []*node{child} - n.wildChild = true n = child n.priority++ numParams-- // if the path doesn't end with the wildcard, then there // will be another non-wildcard subpath starting with '/' - if end < max { - n.path = path[offset:end] - offset = end + if len(wildcard) < len(path) { + path = path[len(wildcard):] child := &node{ maxParams: numParams, @@ -337,58 +346,63 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle } n.children = []*node{child} n = child + continue } - } else { // catchAll - if end != max || numParams > 1 { - panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'") - } - - if len(n.path) > 0 && n.path[len(n.path)-1] == '/' { - panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'") - } - - // currently fixed width 1 for '/' - i-- - if path[i] != '/' { - panic("no / before catch-all in path '" + fullPath + "'") - } - - n.path = path[offset:i] - - // first node: catchAll node with empty path - child := &node{ - wildChild: true, - nType: catchAll, - maxParams: 1, - fullPath: fullPath, - } - // update maxParams of the parent node - if n.maxParams < 1 { - n.maxParams = 1 - } - n.children = []*node{child} - n.indices = string(path[i]) - n = child - n.priority++ - - // second node: node holding the variable - child = &node{ - path: path[i:], - nType: catchAll, - maxParams: 1, - handlers: handlers, - priority: 1, - fullPath: fullPath, - } - n.children = []*node{child} - + // Otherwise we're done. Insert the handle in the new leaf + n.handlers = handlers return } + + // catchAll + if i+len(wildcard) != len(path) || numParams > 1 { + panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'") + } + + if len(n.path) > 0 && n.path[len(n.path)-1] == '/' { + panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'") + } + + // currently fixed width 1 for '/' + i-- + if path[i] != '/' { + panic("no / before catch-all in path '" + fullPath + "'") + } + + n.path = path[:i] + + // First node: catchAll node with empty path + child := &node{ + wildChild: true, + nType: catchAll, + maxParams: 1, + fullPath: fullPath, + } + // update maxParams of the parent node + if n.maxParams < 1 { + n.maxParams = 1 + } + n.children = []*node{child} + n.indices = string('/') + n = child + n.priority++ + + // second node: node holding the variable + child = &node{ + path: path[i:], + nType: catchAll, + maxParams: 1, + handlers: handlers, + priority: 1, + fullPath: fullPath, + } + n.children = []*node{child} + + return } - // insert remaining path part and handle to the leaf - n.path = path[offset:] + // If no wildcard was found, simple insert the path and handle + n.path = path n.handlers = handlers n.fullPath = fullPath } From aee83e040b8f883ea98e3c1017d93db8ccc51c3d Mon Sep 17 00:00:00 2001 From: Lin Kao-Yuan Date: Wed, 18 Dec 2019 09:44:33 +0800 Subject: [PATCH 37/90] Fix "Custom Validators" example (#2186) * Update fixed error code from merged commit According to [this](https://github.com/gin-gonic/examples/commit/874dcfa6c457aa23996d67fa595a2acb8ea1f44b) merged commit. * Fixed incorrect testing date. Original testing date incompatible demo require, can't get expect result. check_in date need NOT AFTER check_out date. --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f8bc4239..7e3e6f41 100644 --- a/README.md +++ b/README.md @@ -704,25 +704,22 @@ package main import ( "net/http" - "reflect" "time" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" - "gopkg.in/go-playground/validator.v8" + "gopkg.in/go-playground/validator.v9" ) // Booking contains binded and validated data. type Booking struct { - CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` + CheckIn time.Time `form:"check_in" binding:"required" 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 { +var bookableDate validator.Func = func(fl validator.FieldLevel) bool { + date, ok := fl.Field().Interface().(time.Time) + if ok { today := time.Now() if today.After(date) { return false @@ -756,7 +753,7 @@ func getBookable(c *gin.Context) { $ curl "localhost:8085/bookable?check_in=2018-04-16&check_out=2018-04-17" {"message":"Booking dates are valid!"} -$ curl "localhost:8085/bookable?check_in=2018-03-08&check_out=2018-03-09" +$ curl "localhost:8085/bookable?check_in=2018-03-10&check_out=2018-03-09" {"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"} ``` From d6143d8d7c0c63d9bedc84f70c5d719f9dbf599b Mon Sep 17 00:00:00 2001 From: thinkerou Date: Wed, 18 Dec 2019 16:58:38 +0800 Subject: [PATCH 38/90] tree: remove one else statement (#2177) --- tree.go | 207 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 103 insertions(+), 104 deletions(-) diff --git a/tree.go b/tree.go index b09d3f67..89f74deb 100644 --- a/tree.go +++ b/tree.go @@ -425,110 +425,7 @@ func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue) walk: // Outer loop for walking the tree for { prefix := n.path - if len(path) > len(prefix) { - if path[:len(prefix)] == prefix { - path = path[len(prefix):] - // If this node does not have a wildcard (param or catchAll) - // child, we can just look up the next child node and continue - // to walk down the tree - if !n.wildChild { - c := path[0] - indices := n.indices - for i, max := 0, len(indices); i < max; i++ { - if c == indices[i] { - n = n.children[i] - prefix = n.path - continue walk - } - } - - // Nothing found. - // We can recommend to redirect to the same URL without a - // trailing slash if a leaf exists for that path. - value.tsr = path == "/" && n.handlers != nil - return - } - - // handle wildcard child - n = n.children[0] - switch n.nType { - case param: - // find param end (either '/' or path end) - end := 0 - for end < len(path) && path[end] != '/' { - end++ - } - - // save param value - if cap(value.params) < int(n.maxParams) { - value.params = make(Params, 0, n.maxParams) - } - i := len(value.params) - value.params = value.params[:i+1] // expand slice within preallocated capacity - value.params[i].Key = n.path[1:] - val := path[:end] - if unescape { - var err error - if value.params[i].Value, err = url.QueryUnescape(val); err != nil { - value.params[i].Value = val // fallback, in case of error - } - } else { - value.params[i].Value = val - } - - // we need to go deeper! - if end < len(path) { - if len(n.children) > 0 { - path = path[end:] - n = n.children[0] - prefix = n.path - continue walk - } - - // ... but we can't - value.tsr = len(path) == end+1 - return - } - - if value.handlers = n.handlers; value.handlers != nil { - value.fullPath = n.fullPath - return - } - if len(n.children) == 1 { - // No handle found. Check if a handle for this path + a - // trailing slash exists for TSR recommendation - n = n.children[0] - value.tsr = n.path == "/" && n.handlers != nil - } - - return - - case catchAll: - // save param value - if cap(value.params) < int(n.maxParams) { - value.params = make(Params, 0, n.maxParams) - } - i := len(value.params) - value.params = value.params[:i+1] // expand slice within preallocated capacity - value.params[i].Key = n.path[2:] - if unescape { - var err error - if value.params[i].Value, err = url.QueryUnescape(path); err != nil { - value.params[i].Value = path // fallback, in case of error - } - } else { - value.params[i].Value = path - } - - value.handlers = n.handlers - value.fullPath = n.fullPath - return - - default: - panic("invalid node type") - } - } - } else if path == prefix { + if path == prefix { // We should have reached the node containing the handle. // Check if this node has a handle registered. if value.handlers = n.handlers; value.handlers != nil { @@ -556,6 +453,108 @@ walk: // Outer loop for walking the tree return } + if len(path) > len(prefix) && path[:len(prefix)] == prefix { + path = path[len(prefix):] + // If this node does not have a wildcard (param or catchAll) + // child, we can just look up the next child node and continue + // to walk down the tree + if !n.wildChild { + c := path[0] + indices := n.indices + for i, max := 0, len(indices); i < max; i++ { + if c == indices[i] { + n = n.children[i] + prefix = n.path + continue walk + } + } + + // Nothing found. + // We can recommend to redirect to the same URL without a + // trailing slash if a leaf exists for that path. + value.tsr = path == "/" && n.handlers != nil + return + } + + // handle wildcard child + n = n.children[0] + switch n.nType { + case param: + // find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ + } + + // save param value + if cap(value.params) < int(n.maxParams) { + value.params = make(Params, 0, n.maxParams) + } + i := len(value.params) + value.params = value.params[:i+1] // expand slice within preallocated capacity + value.params[i].Key = n.path[1:] + val := path[:end] + if unescape { + var err error + if value.params[i].Value, err = url.QueryUnescape(val); err != nil { + value.params[i].Value = val // fallback, in case of error + } + } else { + value.params[i].Value = val + } + + // we need to go deeper! + if end < len(path) { + if len(n.children) > 0 { + path = path[end:] + n = n.children[0] + prefix = n.path + continue walk + } + + // ... but we can't + value.tsr = len(path) == end+1 + return + } + + if value.handlers = n.handlers; value.handlers != nil { + value.fullPath = n.fullPath + return + } + if len(n.children) == 1 { + // No handle found. Check if a handle for this path + a + // trailing slash exists for TSR recommendation + n = n.children[0] + value.tsr = n.path == "/" && n.handlers != nil + } + return + + case catchAll: + // save param value + if cap(value.params) < int(n.maxParams) { + value.params = make(Params, 0, n.maxParams) + } + i := len(value.params) + value.params = value.params[:i+1] // expand slice within preallocated capacity + value.params[i].Key = n.path[2:] + if unescape { + var err error + if value.params[i].Value, err = url.QueryUnescape(path); err != nil { + value.params[i].Value = path // fallback, in case of error + } + } else { + value.params[i].Value = path + } + + value.handlers = n.handlers + value.fullPath = n.fullPath + return + + default: + panic("invalid node type") + } + } + // Nothing found. We can recommend to redirect to the same URL with an // extra trailing slash if a leaf exists for that path value.tsr = (path == "/") || From 1b480ed294cb6d8727e95534af6e668a40231dbd Mon Sep 17 00:00:00 2001 From: Lin Kao-Yuan Date: Wed, 18 Dec 2019 21:08:58 +0800 Subject: [PATCH 39/90] Update to currently output (#2188) Excuse me, I forgot change output in #2186 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e3e6f41..01c4089a 100644 --- a/README.md +++ b/README.md @@ -754,7 +754,7 @@ $ curl "localhost:8085/bookable?check_in=2018-04-16&check_out=2018-04-17" {"message":"Booking dates are valid!"} $ curl "localhost:8085/bookable?check_in=2018-03-10&check_out=2018-03-09" -{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"} +{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"} ``` [Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registered this way. From cc14a770cd11fbd0d1aa1a0a895e69ce8cd1415a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=9E=E9=9B=AA=E6=97=A0=E6=83=85?= Date: Thu, 19 Dec 2019 11:21:58 +0800 Subject: [PATCH 40/90] upgrade go-validator to v10 for README (#2189) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01c4089a..092fedc8 100644 --- a/README.md +++ b/README.md @@ -584,7 +584,7 @@ func main() { To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz). -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). +Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags). 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"`. From 9b3477ef9d2c6a611c9c00cc18aba5a6ba6a7641 Mon Sep 17 00:00:00 2001 From: Lin Kao-Yuan Date: Fri, 20 Dec 2019 14:01:58 +0800 Subject: [PATCH 41/90] Update validator to v10 (#2190) Passed my manual test, output nothing different. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 092fedc8..5c77b282 100644 --- a/README.md +++ b/README.md @@ -708,7 +708,7 @@ import ( "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" - "gopkg.in/go-playground/validator.v9" + "gopkg.in/go-playground/validator.v10" ) // Booking contains binded and validated data. From 59ab588bf597f9f41faee4f217b5659893c2e925 Mon Sep 17 00:00:00 2001 From: John Bampton Date: Mon, 30 Dec 2019 23:55:08 +1000 Subject: [PATCH 42/90] Remove broken link from README. (#2198) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5c77b282..6e0ceb27 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi ## Contents - [Installation](#installation) -- [Prerequisite](#prerequisite) - [Quick start](#quick-start) - [Benchmarks](#benchmarks) - [Gin v1.stable](#gin-v1-stable) From b8a7b6d1945db03a70de86c5837caa93486d1f99 Mon Sep 17 00:00:00 2001 From: John Bampton Date: Tue, 7 Jan 2020 11:19:49 +1000 Subject: [PATCH 43/90] Fix spelling (#2202) --- CHANGELOG.md | 4 ++-- context.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bb90f22..1ceb919c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -196,7 +196,7 @@ - [PERFORMANCE] Misc code optimizations. Inlining, tail call optimizations - [NEW] Built-in support for golang.org/x/net/context - [NEW] Any(path, handler). Create a route that matches any path -- [NEW] Refactored rendering pipeline (faster and static typeded) +- [NEW] Refactored rendering pipeline (faster and static typed) - [NEW] Refactored errors API - [NEW] IndentedJSON() prints pretty JSON - [NEW] Added gin.DefaultWriter @@ -295,7 +295,7 @@ - [FIX] Recovery() middleware only prints panics - [FIX] Context.Get() does not panic anymore. Use MustGet() instead. - [FIX] Multiple http.WriteHeader() in NotFound handlers -- [FIX] Engine.Run() panics if http server can't be setted up +- [FIX] Engine.Run() panics if http server can't be set up - [FIX] Crash when route path doesn't start with '/' - [FIX] Do not update header when status code is negative - [FIX] Setting response headers before calling WriteHeader in context.String() diff --git a/context.go b/context.go index 046f284e..9be222fc 100644 --- a/context.go +++ b/context.go @@ -1003,20 +1003,20 @@ func (c *Context) NegotiateFormat(offered ...string) string { return offered[0] } for _, accepted := range c.Accepted { - for _, offert := range offered { + for _, offer := range offered { // According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers, // therefore we can just iterate over the string without casting it into []rune i := 0 for ; i < len(accepted); i++ { - if accepted[i] == '*' || offert[i] == '*' { - return offert + if accepted[i] == '*' || offer[i] == '*' { + return offer } - if accepted[i] != offert[i] { + if accepted[i] != offer[i] { break } } if i == len(accepted) { - return offert + return offer } } } From fd8a65b2529cb17f1026b10872281f22911846ad Mon Sep 17 00:00:00 2001 From: Antoine GIRARD Date: Tue, 7 Jan 2020 04:31:10 +0100 Subject: [PATCH 44/90] Add build tag nomsgpack (#1852) * add build tag nomsgpack * Update copyright * Update copyright --- .travis.yml | 3 + Makefile | 3 +- binding/binding.go | 2 + binding/binding_msgpack_test.go | 57 ++++++++++++++++ binding/binding_nomsgpack.go | 111 ++++++++++++++++++++++++++++++++ binding/binding_test.go | 41 ------------ binding/msgpack.go | 2 + binding/msgpack_test.go | 2 + render/msgpack.go | 6 ++ render/render.go | 1 - render/render_msgpack_test.go | 43 +++++++++++++ render/render_test.go | 26 -------- 12 files changed, 228 insertions(+), 69 deletions(-) create mode 100644 binding/binding_msgpack_test.go create mode 100644 binding/binding_nomsgpack.go create mode 100644 render/render_msgpack_test.go diff --git a/.travis.yml b/.travis.yml index b80b2577..582b7329 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,9 @@ matrix: - go: 1.12.x env: GO111MODULE=on - go: 1.13.x + - go: 1.13.x + env: + - TESTTAGS=nomsgpack - go: master git: diff --git a/Makefile b/Makefile index e69dbd8b..1a991939 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,13 @@ PACKAGES ?= $(shell $(GO) list ./...) VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /examples/) GOFILES := $(shell find . -name "*.go") TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | grep -v examples) +TESTTAGS ?= "" .PHONY: test test: echo "mode: count" > coverage.out for d in $(TESTFOLDER); do \ - $(GO) test -v -covermode=count -coverprofile=profile.out $$d > tmp.out; \ + $(GO) test -tags $(TESTTAGS) -v -covermode=count -coverprofile=profile.out $$d > tmp.out; \ cat tmp.out; \ if grep -q "^--- FAIL" tmp.out; then \ rm tmp.out; \ diff --git a/binding/binding.go b/binding/binding.go index f578aa55..57562845 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. +// +build !nomsgpack + package binding import "net/http" diff --git a/binding/binding_msgpack_test.go b/binding/binding_msgpack_test.go new file mode 100644 index 00000000..9791a607 --- /dev/null +++ b/binding/binding_msgpack_test.go @@ -0,0 +1,57 @@ +// Copyright 2020 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +// +build !nomsgpack + +package binding + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/ugorji/go/codec" +) + +func TestBindingMsgPack(t *testing.T) { + test := FooStruct{ + Foo: "bar", + } + + h := new(codec.MsgpackHandle) + assert.NotNil(t, h) + buf := bytes.NewBuffer([]byte{}) + assert.NotNil(t, buf) + err := codec.NewEncoder(buf, h).Encode(test) + assert.NoError(t, err) + + data := buf.Bytes() + + testMsgPackBodyBinding(t, + MsgPack, "msgpack", + "/", "/", + string(data), string(data[1:])) +} + +func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { + assert.Equal(t, name, b.Name()) + + obj := FooStruct{} + req := requestWithBody("POST", path, body) + req.Header.Add("Content-Type", MIMEMSGPACK) + err := b.Bind(req, &obj) + assert.NoError(t, err) + assert.Equal(t, "bar", obj.Foo) + + obj = FooStruct{} + req = requestWithBody("POST", badPath, badBody) + req.Header.Add("Content-Type", MIMEMSGPACK) + err = MsgPack.Bind(req, &obj) + assert.Error(t, err) +} + +func TestBindingDefaultMsgPack(t *testing.T) { + assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK)) + assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2)) +} diff --git a/binding/binding_nomsgpack.go b/binding/binding_nomsgpack.go new file mode 100644 index 00000000..fd227b11 --- /dev/null +++ b/binding/binding_nomsgpack.go @@ -0,0 +1,111 @@ +// Copyright 2020 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +// +build nomsgpack + +package binding + +import "net/http" + +// Content-Type MIME of the most common data formats. +const ( + MIMEJSON = "application/json" + MIMEHTML = "text/html" + MIMEXML = "application/xml" + MIMEXML2 = "text/xml" + MIMEPlain = "text/plain" + MIMEPOSTForm = "application/x-www-form-urlencoded" + MIMEMultipartPOSTForm = "multipart/form-data" + MIMEPROTOBUF = "application/x-protobuf" + MIMEYAML = "application/x-yaml" +) + +// Binding describes the interface which needs to be implemented for binding the +// data present in the request such as JSON request body, query parameters or +// the form POST. +type Binding interface { + Name() string + Bind(*http.Request, interface{}) error +} + +// BindingBody adds BindBody method to Binding. BindBody is similar with Bind, +// but it reads the body from supplied bytes instead of req.Body. +type BindingBody interface { + Binding + BindBody([]byte, interface{}) error +} + +// BindingUri adds BindUri method to Binding. BindUri is similar with Bind, +// but it read the Params. +type BindingUri interface { + Name() string + BindUri(map[string][]string, interface{}) error +} + +// StructValidator is the minimal interface which needs to be implemented in +// order for it to be used as the validator engine for ensuring the correctness +// of the request. Gin provides a default implementation for this using +// https://github.com/go-playground/validator/tree/v8.18.2. +type StructValidator interface { + // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right. + // If the received type is not a struct, any validation should be skipped and nil must be returned. + // If the received type is a struct or pointer to a struct, the validation should be performed. + // If the struct is not valid or the validation itself fails, a descriptive error should be returned. + // Otherwise nil must be returned. + ValidateStruct(interface{}) error + + // Engine returns the underlying validator engine which powers the + // StructValidator implementation. + Engine() interface{} +} + +// Validator is the default validator which implements the StructValidator +// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2 +// under the hood. +var Validator StructValidator = &defaultValidator{} + +// These implement the Binding interface and can be used to bind the data +// present in the request to struct instances. +var ( + JSON = jsonBinding{} + XML = xmlBinding{} + Form = formBinding{} + Query = queryBinding{} + FormPost = formPostBinding{} + FormMultipart = formMultipartBinding{} + ProtoBuf = protobufBinding{} + YAML = yamlBinding{} + Uri = uriBinding{} + Header = headerBinding{} +) + +// Default returns the appropriate Binding instance based on the HTTP method +// and the content type. +func Default(method, contentType string) Binding { + if method == "GET" { + return Form + } + + switch contentType { + case MIMEJSON: + return JSON + case MIMEXML, MIMEXML2: + return XML + case MIMEPROTOBUF: + return ProtoBuf + case MIMEYAML: + return YAML + case MIMEMultipartPOSTForm: + return FormMultipart + default: // case MIMEPOSTForm: + return Form + } +} + +func validate(obj interface{}) error { + if Validator == nil { + return nil + } + return Validator.ValidateStruct(obj) +} diff --git a/binding/binding_test.go b/binding/binding_test.go index f0b6f795..4424bab9 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -21,7 +21,6 @@ import ( "github.com/gin-gonic/gin/testdata/protoexample" "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" - "github.com/ugorji/go/codec" ) type appkey struct { @@ -163,9 +162,6 @@ func TestBindingDefault(t *testing.T) { assert.Equal(t, ProtoBuf, Default("POST", MIMEPROTOBUF)) assert.Equal(t, ProtoBuf, Default("PUT", MIMEPROTOBUF)) - assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK)) - assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2)) - assert.Equal(t, YAML, Default("POST", MIMEYAML)) assert.Equal(t, YAML, Default("PUT", MIMEYAML)) } @@ -633,26 +629,6 @@ func TestBindingProtoBufFail(t *testing.T) { string(data), string(data[1:])) } -func TestBindingMsgPack(t *testing.T) { - test := FooStruct{ - Foo: "bar", - } - - h := new(codec.MsgpackHandle) - assert.NotNil(t, h) - buf := bytes.NewBuffer([]byte{}) - assert.NotNil(t, buf) - err := codec.NewEncoder(buf, h).Encode(test) - assert.NoError(t, err) - - data := buf.Bytes() - - testMsgPackBodyBinding(t, - MsgPack, "msgpack", - "/", "/", - string(data), string(data[1:])) -} - func TestValidationFails(t *testing.T) { var obj FooStruct req := requestWithBody("POST", "/", `{"bar": "foo"}`) @@ -1250,23 +1226,6 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body assert.Error(t, err) } -func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { - assert.Equal(t, name, b.Name()) - - obj := FooStruct{} - req := requestWithBody("POST", path, body) - req.Header.Add("Content-Type", MIMEMSGPACK) - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, "bar", obj.Foo) - - obj = FooStruct{} - req = requestWithBody("POST", badPath, badBody) - req.Header.Add("Content-Type", MIMEMSGPACK) - err = MsgPack.Bind(req, &obj) - assert.Error(t, err) -} - func requestWithBody(method, path, body string) (req *http.Request) { req, _ = http.NewRequest(method, path, bytes.NewBufferString(body)) return diff --git a/binding/msgpack.go b/binding/msgpack.go index b7f73197..a5bc2ad2 100644 --- a/binding/msgpack.go +++ b/binding/msgpack.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. +// +build !nomsgpack + package binding import ( diff --git a/binding/msgpack_test.go b/binding/msgpack_test.go index 6baa6739..296d3eb1 100644 --- a/binding/msgpack_test.go +++ b/binding/msgpack_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. +// +build !nomsgpack + package binding import ( diff --git a/render/msgpack.go b/render/msgpack.go index dc681fcf..be2d45c5 100644 --- a/render/msgpack.go +++ b/render/msgpack.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. +// +build !nomsgpack + package render import ( @@ -10,6 +12,10 @@ import ( "github.com/ugorji/go/codec" ) +var ( + _ Render = MsgPack{} +) + // MsgPack contains the given interface object. type MsgPack struct { Data interface{} diff --git a/render/render.go b/render/render.go index abfc79fc..bcd568bf 100644 --- a/render/render.go +++ b/render/render.go @@ -27,7 +27,6 @@ var ( _ HTMLRender = HTMLDebug{} _ HTMLRender = HTMLProduction{} _ Render = YAML{} - _ Render = MsgPack{} _ Render = Reader{} _ Render = AsciiJSON{} _ Render = ProtoBuf{} diff --git a/render/render_msgpack_test.go b/render/render_msgpack_test.go new file mode 100644 index 00000000..e439ac48 --- /dev/null +++ b/render/render_msgpack_test.go @@ -0,0 +1,43 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +// +build !nomsgpack + +package render + +import ( + "bytes" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/ugorji/go/codec" +) + +// TODO unit tests +// test errors + +func TestRenderMsgPack(t *testing.T) { + w := httptest.NewRecorder() + data := map[string]interface{}{ + "foo": "bar", + } + + (MsgPack{data}).WriteContentType(w) + assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type")) + + err := (MsgPack{data}).Render(w) + + assert.NoError(t, err) + + h := new(codec.MsgpackHandle) + assert.NotNil(t, h) + buf := bytes.NewBuffer([]byte{}) + assert.NotNil(t, buf) + err = codec.NewEncoder(buf, h).Encode(data) + + assert.NoError(t, err) + assert.Equal(t, w.Body.String(), string(buf.Bytes())) + assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type")) +} diff --git a/render/render_test.go b/render/render_test.go index 376733df..d0b56152 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -5,7 +5,6 @@ package render import ( - "bytes" "encoding/xml" "errors" "html/template" @@ -17,7 +16,6 @@ import ( "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" - "github.com/ugorji/go/codec" testdata "github.com/gin-gonic/gin/testdata/protoexample" ) @@ -25,30 +23,6 @@ import ( // TODO unit tests // test errors -func TestRenderMsgPack(t *testing.T) { - w := httptest.NewRecorder() - data := map[string]interface{}{ - "foo": "bar", - } - - (MsgPack{data}).WriteContentType(w) - assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type")) - - err := (MsgPack{data}).Render(w) - - assert.NoError(t, err) - - h := new(codec.MsgpackHandle) - assert.NotNil(t, h) - buf := bytes.NewBuffer([]byte{}) - assert.NotNil(t, buf) - err = codec.NewEncoder(buf, h).Encode(data) - - assert.NoError(t, err) - assert.Equal(t, w.Body.String(), buf.String()) - assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type")) -} - func TestRenderJSON(t *testing.T) { w := httptest.NewRecorder() data := map[string]interface{}{ From 025950afe980be14531c51c2790dcb966da1d3fd Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Tue, 7 Jan 2020 17:37:18 +0800 Subject: [PATCH 45/90] Reuse bytes when cleaning the URL paths (#2179) * path: use stack buffer in CleanPath to avoid allocs in common case Sync from https://github.com/julienschmidt/httprouter/commit/8222db13dbb3b3ab1eb84edb61a7030708b93bfa * path: sync test code from httprouter * path: update path_test.go to the latest code Co-authored-by: Bo-Yi Wu --- path.go | 33 +++++++++++++++++++------- path_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 83 insertions(+), 15 deletions(-) diff --git a/path.go b/path.go index d1f59622..a6bac7bd 100644 --- a/path.go +++ b/path.go @@ -5,6 +5,8 @@ package gin +const stackBufSize = 128 + // cleanPath is the URL version of path.Clean, it returns a canonical URL path // for p, eliminating . and .. elements. // @@ -24,8 +26,11 @@ func cleanPath(p string) string { return "/" } + // Reasonably sized buffer on stack to avoid allocations in the common case. + // If a larger buffer is required, it gets allocated dynamically. + buf := make([]byte, 0, stackBufSize) + n := len(p) - var buf []byte // Invariants: // reading from path; r is index of next byte to process. @@ -37,7 +42,12 @@ func cleanPath(p string) string { if p[0] != '/' { r = 0 - buf = make([]byte, n+1) + + if n+1 > stackBufSize { + buf = make([]byte, n+1) + } else { + buf = buf[:n+1] + } buf[0] = '/' } @@ -69,7 +79,7 @@ func cleanPath(p string) string { // can backtrack w-- - if buf == nil { + if len(buf) == 0 { for w > 1 && p[w] != '/' { w-- } @@ -103,7 +113,7 @@ func cleanPath(p string) string { w++ } - if buf == nil { + if len(buf) == 0 { return p[:w] } return string(buf[:w]) @@ -111,13 +121,20 @@ func cleanPath(p string) string { // internal helper to lazily create a buffer if necessary. func bufApp(buf *[]byte, s string, w int, c byte) { - if *buf == nil { + b := *buf + if len(b) == 0 { if s[w] == c { return } - *buf = make([]byte, len(s)) - copy(*buf, s[:w]) + if l := len(s); l > cap(b) { + *buf = make([]byte, len(s)) + } else { + *buf = (*buf)[:l] + } + b = *buf + + copy(b, s[:w]) } - (*buf)[w] = c + b[w] = c } diff --git a/path_test.go b/path_test.go index c1e6ed4f..caefd63a 100644 --- a/path_test.go +++ b/path_test.go @@ -6,15 +6,17 @@ package gin import ( - "runtime" + "strings" "testing" "github.com/stretchr/testify/assert" ) -var cleanTests = []struct { +type cleanPathTest struct { path, result string -}{ +} + +var cleanTests = []cleanPathTest{ // Already clean {"/", "/"}, {"/abc", "/abc"}, @@ -77,13 +79,62 @@ func TestPathCleanMallocs(t *testing.T) { if testing.Short() { t.Skip("skipping malloc count in short mode") } - if runtime.GOMAXPROCS(0) > 1 { - t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") - return - } for _, test := range cleanTests { allocs := testing.AllocsPerRun(100, func() { cleanPath(test.result) }) assert.EqualValues(t, allocs, 0) } } + +func BenchmarkPathClean(b *testing.B) { + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for _, test := range cleanTests { + cleanPath(test.path) + } + } +} + +func genLongPaths() (testPaths []cleanPathTest) { + for i := 1; i <= 1234; i++ { + ss := strings.Repeat("a", i) + + correctPath := "/" + ss + testPaths = append(testPaths, cleanPathTest{ + path: correctPath, + result: correctPath, + }, cleanPathTest{ + path: ss, + result: correctPath, + }, cleanPathTest{ + path: "//" + ss, + result: correctPath, + }, cleanPathTest{ + path: "/" + ss + "/b/..", + result: correctPath, + }) + } + return +} + +func TestPathCleanLong(t *testing.T) { + cleanTests := genLongPaths() + + for _, test := range cleanTests { + assert.Equal(t, test.result, cleanPath(test.path)) + assert.Equal(t, test.result, cleanPath(test.result)) + } +} + +func BenchmarkPathCleanLong(b *testing.B) { + cleanTests := genLongPaths() + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for _, test := range cleanTests { + cleanPath(test.path) + } + } +} From 424e9685bebad809ce5a0cb43d6511e79ad3a878 Mon Sep 17 00:00:00 2001 From: Andrey Abramov Date: Tue, 7 Jan 2020 20:48:28 +0300 Subject: [PATCH 46/90] Update docs on Context.Done(), Context.Deadline() and Context.Err() (#2196) Co-authored-by: Bo-Yi Wu --- context.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/context.go b/context.go index 9be222fc..e979d288 100644 --- a/context.go +++ b/context.go @@ -1032,26 +1032,20 @@ func (c *Context) SetAccepted(formats ...string) { /***** GOLANG.ORG/X/NET/CONTEXT *****/ /************************************/ -// Deadline returns the time when work done on behalf of this context -// should be canceled. Deadline returns ok==false when no deadline is -// set. Successive calls to Deadline return the same results. +// Deadline always returns that there is no deadline (ok==false), +// maybe you want to use Request.Context().Deadline() instead. func (c *Context) Deadline() (deadline time.Time, ok bool) { return } -// Done returns a channel that's closed when work done on behalf of this -// context should be canceled. Done may return nil if this context can -// never be canceled. Successive calls to Done return the same value. +// Done always returns nil (chan which will wait forever), +// if you want to abort your work when the connection was closed +// you should use Request.Context().Done() instead. func (c *Context) Done() <-chan struct{} { return nil } -// Err returns a non-nil error value after Done is closed, -// successive calls to Err return the same error. -// If Done is not yet closed, Err returns nil. -// If Done is closed, Err returns a non-nil error explaining why: -// Canceled if the context was canceled -// or DeadlineExceeded if the context's deadline passed. +// Err always returns nil, maybe you want to use Request.Context().Err() instead. func (c *Context) Err() error { return nil } From ace6e4c2eac3acd6e6df75fa10fff17ee9319382 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Thu, 16 Jan 2020 22:40:59 +0800 Subject: [PATCH 47/90] path: sync code with httprouter (#2212) --- path.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/path.go b/path.go index a6bac7bd..37247d41 100644 --- a/path.go +++ b/path.go @@ -5,8 +5,6 @@ package gin -const stackBufSize = 128 - // cleanPath is the URL version of path.Clean, it returns a canonical URL path // for p, eliminating . and .. elements. // @@ -21,6 +19,7 @@ const stackBufSize = 128 // // If the result of this process is an empty string, "/" is returned. func cleanPath(p string) string { + const stackBufSize = 128 // Turn empty string into "/" if p == "" { return "/" From 982daeb1ecdced87bbef6c11783a640eb88a193a Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sat, 18 Jan 2020 00:32:50 +0800 Subject: [PATCH 48/90] =?UTF-8?q?Use=20zero-copy=20approach=20to=20convert?= =?UTF-8?q?=20types=20between=20string=20and=20byte=E2=80=A6=20(#2206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use zero-copy approach to convert types between string and byte slice * Rename argument to a eligible one Benchmark: BenchmarkBytesConvBytesToStrRaw-4 21003800 70.9 ns/op 96 B/op 1 allocs/op BenchmarkBytesConvBytesToStr-4 1000000000 0.333 ns/op 0 B/op 0 allocs/op BenchmarkBytesConvStrToBytesRaw-4 18478059 59.3 ns/op 96 B/op 1 allocs/op BenchmarkBytesConvStrToBytes-4 1000000000 0.373 ns/op 0 B/op 0 allocs/op Co-authored-by: Bo-Yi Wu --- auth.go | 4 +- binding/form_mapping.go | 5 +- gin.go | 3 +- internal/bytesconv/bytesconv.go | 19 ++++++ internal/bytesconv/bytesconv_test.go | 95 ++++++++++++++++++++++++++++ render/json.go | 14 ++-- 6 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 internal/bytesconv/bytesconv.go create mode 100644 internal/bytesconv/bytesconv_test.go diff --git a/auth.go b/auth.go index c96b1e29..9e5d4cf6 100644 --- a/auth.go +++ b/auth.go @@ -8,6 +8,8 @@ import ( "encoding/base64" "net/http" "strconv" + + "github.com/gin-gonic/gin/internal/bytesconv" ) // AuthUserKey is the cookie name for user credential in basic auth. @@ -83,5 +85,5 @@ func processAccounts(accounts Accounts) authPairs { func authorizationHeader(user, password string) string { base := user + ":" + password - return "Basic " + base64.StdEncoding.EncodeToString([]byte(base)) + return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base)) } diff --git a/binding/form_mapping.go b/binding/form_mapping.go index d6199c4f..b81ad195 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/gin-gonic/gin/internal/bytesconv" "github.com/gin-gonic/gin/internal/json" ) @@ -208,9 +209,9 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel case time.Time: return setTimeField(val, field, value) } - return json.Unmarshal([]byte(val), value.Addr().Interface()) + return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface()) case reflect.Map: - return json.Unmarshal([]byte(val), value.Addr().Interface()) + return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface()) default: return errUnknownType } diff --git a/gin.go b/gin.go index 71f3fd5c..0244f18c 100644 --- a/gin.go +++ b/gin.go @@ -13,6 +13,7 @@ import ( "path" "sync" + "github.com/gin-gonic/gin/internal/bytesconv" "github.com/gin-gonic/gin/render" ) @@ -477,7 +478,7 @@ func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool { rPath := req.URL.Path if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(rPath), trailingSlash); ok { - req.URL.Path = string(fixedPath) + req.URL.Path = bytesconv.BytesToString(fixedPath) redirectRequest(c) return true } diff --git a/internal/bytesconv/bytesconv.go b/internal/bytesconv/bytesconv.go new file mode 100644 index 00000000..32c2b59e --- /dev/null +++ b/internal/bytesconv/bytesconv.go @@ -0,0 +1,19 @@ +package bytesconv + +import ( + "reflect" + "unsafe" +) + +// StringToBytes converts string to byte slice without a memory allocation. +func StringToBytes(s string) (b []byte) { + sh := *(*reflect.StringHeader)(unsafe.Pointer(&s)) + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len + return b +} + +// BytesToString converts byte slice to string without a memory allocation. +func BytesToString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/internal/bytesconv/bytesconv_test.go b/internal/bytesconv/bytesconv_test.go new file mode 100644 index 00000000..ee2c8ab2 --- /dev/null +++ b/internal/bytesconv/bytesconv_test.go @@ -0,0 +1,95 @@ +package bytesconv + +import ( + "bytes" + "math/rand" + "strings" + "testing" + "time" +) + +var testString = "Albert Einstein: Logic will get you from A to B. Imagination will take you everywhere." +var testBytes = []byte(testString) + +func rawBytesToStr(b []byte) string { + return string(b) +} + +func rawStrToBytes(s string) []byte { + return []byte(s) +} + +// go test -v + +func TestBytesToString(t *testing.T) { + data := make([]byte, 1024) + for i := 0; i < 100; i++ { + rand.Read(data) + if rawBytesToStr(data) != BytesToString(data) { + t.Fatal("don't match") + } + } +} + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letterBytes) { + sb.WriteByte(letterBytes[idx]) + i-- + } + cache >>= letterIdxBits + remain-- + } + + return sb.String() +} + +func TestStringToBytes(t *testing.T) { + for i := 0; i < 100; i++ { + s := RandStringBytesMaskImprSrcSB(64) + if !bytes.Equal(rawStrToBytes(s), StringToBytes(s)) { + t.Fatal("don't match") + } + } +} + +// go test -v -run=none -bench=^BenchmarkBytesConv -benchmem=true + +func BenchmarkBytesConvBytesToStrRaw(b *testing.B) { + for i := 0; i < b.N; i++ { + rawBytesToStr(testBytes) + } +} + +func BenchmarkBytesConvBytesToStr(b *testing.B) { + for i := 0; i < b.N; i++ { + BytesToString(testBytes) + } +} + +func BenchmarkBytesConvStrToBytesRaw(b *testing.B) { + for i := 0; i < b.N; i++ { + rawStrToBytes(testString) + } +} + +func BenchmarkBytesConvStrToBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + StringToBytes(testString) + } +} diff --git a/render/json.go b/render/json.go index 70506f78..a6fd3117 100644 --- a/render/json.go +++ b/render/json.go @@ -10,6 +10,7 @@ import ( "html/template" "net/http" + "github.com/gin-gonic/gin/internal/bytesconv" "github.com/gin-gonic/gin/internal/json" ) @@ -97,8 +98,9 @@ func (r SecureJSON) Render(w http.ResponseWriter) error { return err } // if the jsonBytes is array values - if bytes.HasPrefix(jsonBytes, []byte("[")) && bytes.HasSuffix(jsonBytes, []byte("]")) { - _, err = w.Write([]byte(r.Prefix)) + if bytes.HasPrefix(jsonBytes, bytesconv.StringToBytes("[")) && bytes.HasSuffix(jsonBytes, + bytesconv.StringToBytes("]")) { + _, err = w.Write(bytesconv.StringToBytes(r.Prefix)) if err != nil { return err } @@ -126,11 +128,11 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) { } callback := template.JSEscapeString(r.Callback) - _, err = w.Write([]byte(callback)) + _, err = w.Write(bytesconv.StringToBytes(callback)) if err != nil { return err } - _, err = w.Write([]byte("(")) + _, err = w.Write(bytesconv.StringToBytes("(")) if err != nil { return err } @@ -138,7 +140,7 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) { if err != nil { return err } - _, err = w.Write([]byte(");")) + _, err = w.Write(bytesconv.StringToBytes(");")) if err != nil { return err } @@ -160,7 +162,7 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) { } var buffer bytes.Buffer - for _, r := range string(ret) { + for _, r := range bytesconv.BytesToString(ret) { cvt := string(r) if r >= 128 { cvt = fmt.Sprintf("\\u%04x", int64(r)) From f94406a087079bfa5a924cc8095ba753160e885c Mon Sep 17 00:00:00 2001 From: ali Date: Mon, 20 Jan 2020 07:12:44 +0000 Subject: [PATCH 49/90] Added support for SameSite cookie flag (#1615) * Added support for SameSite cookie flag * fixed tests. * Update context.go * Update context_test.go * Update context_test.go Co-authored-by: thinkerou Co-authored-by: Bo-Yi Wu --- context.go | 3 ++- context_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/context.go b/context.go index e979d288..ee202d1e 100644 --- a/context.go +++ b/context.go @@ -775,7 +775,7 @@ func (c *Context) GetRawData() ([]byte, error) { // 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) { +func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, sameSite http.SameSite, secure, httpOnly bool) { if path == "" { path = "/" } @@ -785,6 +785,7 @@ func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, MaxAge: maxAge, Path: path, Domain: domain, + SameSite: sameSite, Secure: secure, HttpOnly: httpOnly, }) diff --git a/context_test.go b/context_test.go index 18709d3d..df2d9543 100644 --- a/context_test.go +++ b/context_test.go @@ -602,14 +602,14 @@ func TestContextPostFormMultipart(t *testing.T) { func TestContextSetCookie(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.SetCookie("user", "gin", 1, "/", "localhost", true, true) - assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure", c.Writer.Header().Get("Set-Cookie")) + c.SetCookie("user", "gin", 1, "/", "localhost", http.SameSiteLaxMode, true, true) + assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie")) } func TestContextSetCookiePathEmpty(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.SetCookie("user", "gin", 1, "", "localhost", true, true) - assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure", c.Writer.Header().Get("Set-Cookie")) + c.SetCookie("user", "gin", 1, "", "localhost", http.SameSiteLaxMode, true, true) + assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie")) } func TestContextGetCookie(t *testing.T) { From 69a202dbbd51819a2c3fa2aa0c65cb83592961bd Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Wed, 22 Jan 2020 00:24:25 +0800 Subject: [PATCH 50/90] chore: upgrade go-isatty and json-iterator/go (#2215) --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 1213bd23..ae98b9e7 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( github.com/gin-contrib/sse v0.1.0 github.com/go-playground/validator/v10 v10.0.1 github.com/golang/protobuf v1.3.2 - github.com/json-iterator/go v1.1.7 - github.com/mattn/go-isatty v0.0.9 + github.com/json-iterator/go v1.1.9 + github.com/mattn/go-isatty v0.0.11 github.com/stretchr/testify v1.4.0 github.com/ugorji/go/codec v1.1.7 gopkg.in/yaml.v2 v2.2.2 diff --git a/go.sum b/go.sum index 9815f2f4..44c34a38 100644 --- a/go.sum +++ b/go.sum @@ -14,12 +14,12 @@ github.com/go-playground/validator/v10 v10.0.1/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= @@ -34,8 +34,8 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= From 07c0f05f244589ff4a02320a01ad0a1fc102cbd5 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Thu, 23 Jan 2020 07:54:08 +0800 Subject: [PATCH 51/90] Renew README to fit the modification of SetCookie method (#2217) fix #2214 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e0ceb27..966ffbc5 100644 --- a/README.md +++ b/README.md @@ -2025,7 +2025,7 @@ func main() { if err != nil { cookie = "NotSet" - c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true) + c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", http.SameSiteLaxMode, false, true) } fmt.Printf("Cookie value: %s \n", cookie) From 64e6a7654f134c18e1a21efa9ccd20c477c84f87 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 28 Jan 2020 11:38:45 +0800 Subject: [PATCH 52/90] docs(path): improve comments (#2223) * chore(path): improve comments copy from https://github.com/julienschmidt/httprouter/commit/15782a78c61201cf2fdbc138d63aa60fff114695 * fix typo Signed-off-by: Bo-Yi Wu --- path.go | 24 +++++++++++++++++------- tree.go | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/path.go b/path.go index 37247d41..51346e4a 100644 --- a/path.go +++ b/path.go @@ -53,8 +53,9 @@ func cleanPath(p string) string { trailing := n > 1 && p[n-1] == '/' // A bit more clunky without a 'lazybuf' like the path package, but the loop - // gets completely inlined (bufApp). So in contrast to the path package this - // loop has no expensive function calls (except 1x make) + // gets completely inlined (bufApp calls). + // loop has no expensive function calls (except 1x make) // So in contrast to the path package this loop has no expensive function + // calls (except make, if needed). for r < n { switch { @@ -90,14 +91,14 @@ func cleanPath(p string) string { } default: - // real path element. - // add slash if needed + // Real path element. + // Add slash if needed if w > 1 { bufApp(&buf, p, w, '/') w++ } - // copy element + // Copy element for r < n && p[r] != '/' { bufApp(&buf, p, w, p[r]) w++ @@ -106,26 +107,35 @@ func cleanPath(p string) string { } } - // re-append trailing slash + // Re-append trailing slash if trailing && w > 1 { bufApp(&buf, p, w, '/') w++ } + // If the original string was not modified (or only shortened at the end), + // return the respective substring of the original string. + // Otherwise return a new string from the buffer. if len(buf) == 0 { return p[:w] } return string(buf[:w]) } -// internal helper to lazily create a buffer if necessary. +// Internal helper to lazily create a buffer if necessary. +// Calls to this function get inlined. func bufApp(buf *[]byte, s string, w int, c byte) { b := *buf if len(b) == 0 { + // No modification of the original string so far. + // If the next character is the same as in the original string, we do + // not yet have to allocate a buffer. if s[w] == c { return } + // Otherwise use either the stack buffer, if it is large enough, or + // allocate a new buffer on the heap, and copy all previous characters. if l := len(s); l > cap(b) { *buf = make([]byte, len(s)) } else { diff --git a/tree.go b/tree.go index 89f74deb..2f6b1890 100644 --- a/tree.go +++ b/tree.go @@ -401,7 +401,7 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle return } - // If no wildcard was found, simple insert the path and handle + // If no wildcard was found, simply insert the path and handle n.path = path n.handlers = handlers n.fullPath = fullPath From 0e4d8eaf07c2d72b548c5157197cbe2115dfb557 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 28 Jan 2020 18:35:47 +0800 Subject: [PATCH 53/90] tree: remove duplicate assignment (#2222) copy from https://github.com/julienschmidt/httprouter/commit/cfa3cb764b4fc4eb98cae67a2020a91c79e065be Co-authored-by: thinkerou --- tree.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/tree.go b/tree.go index 2f6b1890..b88eefa5 100644 --- a/tree.go +++ b/tree.go @@ -464,7 +464,6 @@ walk: // Outer loop for walking the tree for i, max := 0, len(indices); i < max; i++ { if c == indices[i] { n = n.children[i] - prefix = n.path continue walk } } @@ -508,7 +507,6 @@ walk: // Outer loop for walking the tree if len(n.children) > 0 { path = path[end:] n = n.children[0] - prefix = n.path continue walk } From 731c827892f5b1eac9f58fc65cef32fa1908972c Mon Sep 17 00:00:00 2001 From: Erik Bender Date: Thu, 6 Feb 2020 07:50:21 +0100 Subject: [PATCH 54/90] add yaml negotitation (#2220) Co-authored-by: thinkerou --- context.go | 5 +++++ context_test.go | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index ee202d1e..1c1b9639 100644 --- a/context.go +++ b/context.go @@ -970,6 +970,7 @@ type Negotiate struct { HTMLData interface{} JSONData interface{} XMLData interface{} + YAMLData interface{} Data interface{} } @@ -988,6 +989,10 @@ func (c *Context) Negotiate(code int, config Negotiate) { data := chooseData(config.XMLData, config.Data) c.XML(code, data) + case binding.MIMEYAML: + data := chooseData(config.YAMLData, config.Data) + c.YAML(code, data) + default: c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck } diff --git a/context_test.go b/context_test.go index df2d9543..4380fb5b 100644 --- a/context_test.go +++ b/context_test.go @@ -1114,7 +1114,7 @@ func TestContextNegotiationWithJSON(t *testing.T) { c.Request, _ = http.NewRequest("POST", "", nil) c.Negotiate(http.StatusOK, Negotiate{ - Offered: []string{MIMEJSON, MIMEXML}, + Offered: []string{MIMEJSON, MIMEXML, MIMEYAML}, Data: H{"foo": "bar"}, }) @@ -1129,7 +1129,7 @@ func TestContextNegotiationWithXML(t *testing.T) { c.Request, _ = http.NewRequest("POST", "", nil) c.Negotiate(http.StatusOK, Negotiate{ - Offered: []string{MIMEXML, MIMEJSON}, + Offered: []string{MIMEXML, MIMEJSON, MIMEYAML}, Data: H{"foo": "bar"}, }) From acac7b12102c837752340033624720d245ab2734 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 9 Feb 2020 10:46:22 +0800 Subject: [PATCH 55/90] tree: range over nodes values (#2229) --- tree.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tree.go b/tree.go index b88eefa5..b687ec43 100644 --- a/tree.go +++ b/tree.go @@ -169,9 +169,9 @@ walk: } // Update maxParams (max of all children) - for i := range child.children { - if child.children[i].maxParams > child.maxParams { - child.maxParams = child.children[i].maxParams + for _, v := range child.children { + if v.maxParams > child.maxParams { + child.maxParams = v.maxParams } } From 0d12918b0ad1dbf28f61d3d053ae035dbd22a4eb Mon Sep 17 00:00:00 2001 From: thinkerou Date: Thu, 13 Feb 2020 20:23:29 +0800 Subject: [PATCH 56/90] chore: upgrade depend version (#2231) --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index ae98b9e7..cfaee746 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ module github.com/gin-gonic/gin -go 1.12 +go 1.13 require ( github.com/gin-contrib/sse v0.1.0 - github.com/go-playground/validator/v10 v10.0.1 - github.com/golang/protobuf v1.3.2 + github.com/go-playground/validator/v10 v10.2.0 + github.com/golang/protobuf v1.3.3 github.com/json-iterator/go v1.1.9 - github.com/mattn/go-isatty v0.0.11 + github.com/mattn/go-isatty v0.0.12 github.com/stretchr/testify v1.4.0 github.com/ugorji/go/codec v1.1.7 - gopkg.in/yaml.v2 v2.2.2 + gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index 44c34a38..d4998155 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,4 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -9,17 +8,17 @@ github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8c github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.0.1 h1:QgDDZpXlR/L3atIL2PbFt0TpazbtN7N6PxTGcgcyEUg= -github.com/go-playground/validator/v10 v10.0.1/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= @@ -34,11 +33,12 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 863ad2d4deede093860b5234fad9f2a495cc536f Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 21 Feb 2020 16:33:36 +0800 Subject: [PATCH 57/90] docs(badge): add todo badge (#2240) fix https://github.com/gin-gonic/gin/issues/2236 Signed-off-by: Bo-Yi Wu --- README.md | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 966ffbc5..4a0ec90c 100644 --- a/README.md +++ b/README.md @@ -10,30 +10,36 @@ [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge) [![Open Source Helpers](https://www.codetriage.com/gin-gonic/gin/badges/users.svg)](https://www.codetriage.com/gin-gonic/gin) [![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases) +[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/gin-gonic/gin)](https://www.tickgit.com/browse?repo=github.com/gin-gonic/gin) Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. ## Contents -- [Installation](#installation) -- [Quick start](#quick-start) -- [Benchmarks](#benchmarks) -- [Gin v1.stable](#gin-v1-stable) -- [Build with jsoniter](#build-with-jsoniter) -- [API Examples](#api-examples) - - [Using GET,POST,PUT,PATCH,DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options) +- [Gin Web Framework](#gin-web-framework) + - [Contents](#contents) + - [Installation](#installation) + - [Quick start](#quick-start) + - [Benchmarks](#benchmarks) + - [Gin v1. stable](#gin-v1-stable) + - [Build with jsoniter](#build-with-jsoniter) + - [API Examples](#api-examples) + - [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options) - [Parameters in path](#parameters-in-path) - [Querystring parameters](#querystring-parameters) - [Multipart/Urlencoded Form](#multiparturlencoded-form) - [Another example: query + post form](#another-example-query--post-form) - [Map as querystring or postform parameters](#map-as-querystring-or-postform-parameters) - [Upload files](#upload-files) + - [Single file](#single-file) + - [Multiple files](#multiple-files) - [Grouping routes](#grouping-routes) - [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default) - [Using middleware](#using-middleware) - [How to write log file](#how-to-write-log-file) - [Custom Log Format](#custom-log-format) + - [Controlling Log output coloring](#controlling-log-output-coloring) - [Model binding and validation](#model-binding-and-validation) - [Custom Validators](#custom-validators) - [Only Bind Query String](#only-bind-query-string) @@ -43,10 +49,16 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [Bind HTML checkboxes](#bind-html-checkboxes) - [Multipart/Urlencoded binding](#multiparturlencoded-binding) - [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering) - - [JSONP rendering](#jsonp) + - [SecureJSON](#securejson) + - [JSONP](#jsonp) + - [AsciiJSON](#asciijson) + - [PureJSON](#purejson) - [Serving static files](#serving-static-files) - [Serving data from reader](#serving-data-from-reader) - [HTML rendering](#html-rendering) + - [Custom Template renderer](#custom-template-renderer) + - [Custom Delimiters](#custom-delimiters) + - [Custom Template Funcs](#custom-template-funcs) - [Multitemplate](#multitemplate) - [Redirects](#redirects) - [Custom Middleware](#custom-middleware) @@ -62,8 +74,8 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [http2 server push](#http2-server-push) - [Define format for the log of routes](#define-format-for-the-log-of-routes) - [Set and get a cookie](#set-and-get-a-cookie) -- [Testing](#testing) -- [Users](#users) + - [Testing](#testing) + - [Users](#users) ## Installation From 5f56109bcffd58e851dd186282d8fbd4d0decc82 Mon Sep 17 00:00:00 2001 From: Kaushik Neelichetty Date: Fri, 21 Feb 2020 14:45:17 +0530 Subject: [PATCH 58/90] Use json marshall in context json to fix breaking new line issue. Fixes #2209 (#2228) * ignore IntelliJ idea generated files * update JSON renderer to use Marshall() instead of Encode(). Fix #2209 * Revert "ignore IntelliJ idea generated files" This reverts commit e7bd017227df5dbd2ed2f5fe353adb5f1b08c678. Co-authored-by: Bo-Yi Wu Co-authored-by: thinkerou --- context_test.go | 10 +++++----- logger_test.go | 6 +++--- middleware_test.go | 2 +- render/json.go | 7 +++++-- render/render_test.go | 2 +- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/context_test.go b/context_test.go index 4380fb5b..7f0bca3c 100644 --- a/context_test.go +++ b/context_test.go @@ -662,7 +662,7 @@ func TestContextRenderJSON(t *testing.T) { c.JSON(http.StatusCreated, H{"foo": "bar", "html": ""}) assert.Equal(t, http.StatusCreated, w.Code) - assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}\n", w.Body.String()) + assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } @@ -690,7 +690,7 @@ func TestContextRenderJSONPWithoutCallback(t *testing.T) { c.JSONP(http.StatusCreated, H{"foo": "bar"}) assert.Equal(t, http.StatusCreated, w.Code) - assert.Equal(t, "{\"foo\":\"bar\"}\n", w.Body.String()) + assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } @@ -716,7 +716,7 @@ func TestContextRenderAPIJSON(t *testing.T) { c.JSON(http.StatusCreated, H{"foo": "bar"}) assert.Equal(t, http.StatusCreated, w.Code) - assert.Equal(t, "{\"foo\":\"bar\"}\n", w.Body.String()) + assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type")) } @@ -1119,7 +1119,7 @@ func TestContextNegotiationWithJSON(t *testing.T) { }) assert.Equal(t, http.StatusOK, w.Code) - assert.Equal(t, "{\"foo\":\"bar\"}\n", w.Body.String()) + assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } @@ -1283,7 +1283,7 @@ func TestContextAbortWithStatusJSON(t *testing.T) { _, err := buf.ReadFrom(w.Body) assert.NoError(t, err) jsonStringBody := buf.String() - assert.Equal(t, fmt.Sprint("{\"foo\":\"fooValue\",\"bar\":\"barValue\"}\n"), jsonStringBody) + assert.Equal(t, fmt.Sprint("{\"foo\":\"fooValue\",\"bar\":\"barValue\"}"), jsonStringBody) } func TestContextError(t *testing.T) { diff --git a/logger_test.go b/logger_test.go index fc53f356..b587f89e 100644 --- a/logger_test.go +++ b/logger_test.go @@ -369,15 +369,15 @@ func TestErrorLogger(t *testing.T) { w := performRequest(router, "GET", "/error") assert.Equal(t, http.StatusOK, w.Code) - assert.Equal(t, "{\"error\":\"this is an error\"}\n", w.Body.String()) + assert.Equal(t, "{\"error\":\"this is an error\"}", w.Body.String()) w = performRequest(router, "GET", "/abort") assert.Equal(t, http.StatusUnauthorized, w.Code) - assert.Equal(t, "{\"error\":\"no authorized\"}\n", w.Body.String()) + assert.Equal(t, "{\"error\":\"no authorized\"}", w.Body.String()) w = performRequest(router, "GET", "/print") assert.Equal(t, http.StatusInternalServerError, w.Code) - assert.Equal(t, "hola!{\"error\":\"this is an error\"}\n", w.Body.String()) + assert.Equal(t, "hola!{\"error\":\"this is an error\"}", w.Body.String()) } func TestLoggerWithWriterSkippingPaths(t *testing.T) { diff --git a/middleware_test.go b/middleware_test.go index 2ae9e889..fca1c530 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -246,5 +246,5 @@ func TestMiddlewareWrite(t *testing.T) { w := performRequest(router, "GET", "/") assert.Equal(t, http.StatusBadRequest, w.Code) - assert.Equal(t, strings.Replace("hola\nbar{\"foo\":\"bar\"}\n{\"foo\":\"bar\"}\nevent:test\ndata:message\n\n", " ", "", -1), strings.Replace(w.Body.String(), " ", "", -1)) + assert.Equal(t, strings.Replace("hola\nbar{\"foo\":\"bar\"}{\"foo\":\"bar\"}event:test\ndata:message\n\n", " ", "", -1), strings.Replace(w.Body.String(), " ", "", -1)) } diff --git a/render/json.go b/render/json.go index a6fd3117..015c0dbb 100644 --- a/render/json.go +++ b/render/json.go @@ -69,8 +69,11 @@ func (r JSON) WriteContentType(w http.ResponseWriter) { // WriteJSON marshals the given interface object and writes it with custom ContentType. func WriteJSON(w http.ResponseWriter, obj interface{}) error { writeContentType(w, jsonContentType) - encoder := json.NewEncoder(w) - err := encoder.Encode(&obj) + jsonBytes, err := json.Marshal(obj) + if err != nil { + return err + } + _, err = w.Write(jsonBytes) return err } diff --git a/render/render_test.go b/render/render_test.go index d0b56152..353c82bb 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -36,7 +36,7 @@ func TestRenderJSON(t *testing.T) { err := (JSON{data}).Render(w) assert.NoError(t, err) - assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}\n", w.Body.String()) + assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) } From 094b3fdb393ff8eab16ec293bf49513213955523 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Wed, 26 Feb 2020 10:27:03 +0800 Subject: [PATCH 59/90] ci support go1.14 (#2262) --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 582b7329..6680a5b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,10 @@ matrix: - go: 1.13.x env: - TESTTAGS=nomsgpack + - go: 1.14.x + - go: 1.14.x + env: + - TESTTAGS=nomsgpack - go: master git: From 2ff2b19e14420de970264012e1bcdf91929725b4 Mon Sep 17 00:00:00 2001 From: kebo Date: Sat, 7 Mar 2020 09:21:02 +0800 Subject: [PATCH 60/90] fix accept incoming network connections (#2216) --- utils.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils.go b/utils.go index 71b80de7..77b5f0c8 100644 --- a/utils.go +++ b/utils.go @@ -139,10 +139,10 @@ func resolveAddress(addr []string) string { case 0: if port := os.Getenv("PORT"); port != "" { debugPrint("Environment variable PORT=\"%s\"", port) - return ":" + port + return "localhost:" + port } debugPrint("Environment variable PORT is undefined. Using port :8080 by default") - return ":8080" + return "localhost:8080" case 1: return addr[0] default: From 1d055af1bc15ab3c5965ce0bf99c6f3f44465b56 Mon Sep 17 00:00:00 2001 From: Nikifor Seryakov Date: Sat, 7 Mar 2020 05:23:33 +0300 Subject: [PATCH 61/90] FileFromFS (#2112) * Context.FileFromFS added * Context File and FileFromFS examples at README Co-authored-by: Bo-Yi Wu --- README.md | 18 ++++++++++++++++++ context.go | 11 +++++++++++ context_test.go | 13 +++++++++++++ 3 files changed, 42 insertions(+) diff --git a/README.md b/README.md index 4a0ec90c..709a15bc 100644 --- a/README.md +++ b/README.md @@ -1182,6 +1182,24 @@ func main() { } ``` +### Serving data from file + +```go +func main() { + router := gin.Default() + + router.GET("/local/file", func(c *gin.Context) { + c.File("local/file.go") + }) + + var fs http.FileSystem = // ... + router.GET("/fs/file", func(c *gin.Context) { + c.FileFromFS("fs/file.go", fs) + }) +} + +``` + ### Serving data from reader ```go diff --git a/context.go b/context.go index 1c1b9639..1d3e6652 100644 --- a/context.go +++ b/context.go @@ -925,6 +925,17 @@ func (c *Context) File(filepath string) { http.ServeFile(c.Writer, c.Request, filepath) } +// FileFromFS writes the specified file from http.FileSytem into the body stream in an efficient way. +func (c *Context) FileFromFS(filepath string, fs http.FileSystem) { + defer func(old string) { + c.Request.URL.Path = old + }(c.Request.URL.Path) + + c.Request.URL.Path = filepath + + http.FileServer(fs).ServeHTTP(c.Writer, c.Request) +} + // FileAttachment writes the specified file into the body stream in an efficient way // On the client side, the file will typically be downloaded with the given filename func (c *Context) FileAttachment(filepath, filename string) { diff --git a/context_test.go b/context_test.go index 7f0bca3c..78b22c0d 100644 --- a/context_test.go +++ b/context_test.go @@ -992,6 +992,19 @@ func TestContextRenderFile(t *testing.T) { assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) } +func TestContextRenderFileFromFS(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("GET", "/some/path", nil) + c.FileFromFS("./gin.go", Dir(".", false)) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Contains(t, w.Body.String(), "func New() *Engine {") + assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) + assert.Equal(t, "/some/path", c.Request.URL.Path) +} + func TestContextRenderAttachment(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) From a71af9c144f9579f6dbe945341c1df37aaf09c0d Mon Sep 17 00:00:00 2001 From: Manuel Alonso Date: Sat, 7 Mar 2020 14:51:33 +0100 Subject: [PATCH 62/90] removing log injection (#2277) Co-authored-by: thinkerou --- logger.go | 2 +- logger_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/logger.go b/logger.go index d5b96b3e..d361b74d 100644 --- a/logger.go +++ b/logger.go @@ -141,7 +141,7 @@ var defaultLogFormatter = func(param LogFormatterParams) string { // Truncate in a golang < 1.8 safe way param.Latency = param.Latency - param.Latency%time.Second } - return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s", + return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s", param.TimeStamp.Format("2006/01/02 - 15:04:05"), statusColor, param.StatusCode, resetColor, param.Latency, diff --git a/logger_test.go b/logger_test.go index b587f89e..0d40666e 100644 --- a/logger_test.go +++ b/logger_test.go @@ -158,7 +158,7 @@ func TestLoggerWithFormatter(t *testing.T) { router := New() router.Use(LoggerWithFormatter(func(param LogFormatterParams) string { - return fmt.Sprintf("[FORMATTER TEST] %v | %3d | %13v | %15s | %-7s %s\n%s", + return fmt.Sprintf("[FORMATTER TEST] %v | %3d | %13v | %15s | %-7s %#v\n%s", param.TimeStamp.Format("2006/01/02 - 15:04:05"), param.StatusCode, param.Latency, @@ -275,11 +275,11 @@ func TestDefaultLogFormatter(t *testing.T) { isTerm: false, } - assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 | 200 | 5s | 20.20.20.20 | GET /\n", defaultLogFormatter(termFalseParam)) - assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 | 200 | 2743h29m3s | 20.20.20.20 | GET /\n", defaultLogFormatter(termFalseLongDurationParam)) + assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 | 200 | 5s | 20.20.20.20 | GET \"/\"\n", defaultLogFormatter(termFalseParam)) + assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 | 200 | 2743h29m3s | 20.20.20.20 | GET \"/\"\n", defaultLogFormatter(termFalseLongDurationParam)) - assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 |\x1b[97;42m 200 \x1b[0m| 5s | 20.20.20.20 |\x1b[97;44m GET \x1b[0m /\n", defaultLogFormatter(termTrueParam)) - assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 |\x1b[97;42m 200 \x1b[0m| 2743h29m3s | 20.20.20.20 |\x1b[97;44m GET \x1b[0m /\n", defaultLogFormatter(termTrueLongDurationParam)) + assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 |\x1b[97;42m 200 \x1b[0m| 5s | 20.20.20.20 |\x1b[97;44m GET \x1b[0m \"/\"\n", defaultLogFormatter(termTrueParam)) + assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 |\x1b[97;42m 200 \x1b[0m| 2743h29m3s | 20.20.20.20 |\x1b[97;44m GET \x1b[0m \"/\"\n", defaultLogFormatter(termTrueLongDurationParam)) } From 67008be35f5432aeec06641540f3b6df3eac8747 Mon Sep 17 00:00:00 2001 From: "Ryan J. Yoder" Date: Mon, 16 Mar 2020 07:36:15 -0700 Subject: [PATCH 63/90] Unix Socket Handling (#2280) * do not set unix socket permissions. Cleanup unix socket. * removed useless error checking --- gin.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/gin.go b/gin.go index 0244f18c..ab1d0a46 100644 --- a/gin.go +++ b/gin.go @@ -320,16 +320,13 @@ func (engine *Engine) RunUnix(file string) (err error) { debugPrint("Listening and serving HTTP on unix:/%s", file) defer func() { debugPrintError(err) }() - os.Remove(file) listener, err := net.Listen("unix", file) if err != nil { return } defer listener.Close() - err = os.Chmod(file, 0777) - if err != nil { - return - } + defer os.Remove(file) + err = http.Serve(listener, engine) return } From 73ccfea3ba5a115e74177dbfbc1ea0fff88c13f4 Mon Sep 17 00:00:00 2001 From: AcoNCodes Date: Mon, 16 Mar 2020 18:52:02 +0200 Subject: [PATCH 64/90] Add mutex for protect Context.Keys map (#1391) * Add mutex for protect Context.Keys map * Fix tests Co-authored-by: Nikolay Tolkachov Co-authored-by: Bo-Yi Wu --- context.go | 10 ++++++++++ gin.go | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/context.go b/context.go index 1d3e6652..572dbb81 100644 --- a/context.go +++ b/context.go @@ -16,6 +16,7 @@ import ( "net/url" "os" "strings" + "sync" "time" "github.com/gin-contrib/sse" @@ -52,6 +53,9 @@ type Context struct { engine *Engine + // This mutex protect Keys map + KeysMutex *sync.RWMutex + // Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} @@ -78,6 +82,7 @@ func (c *Context) reset() { c.Params = c.Params[0:0] c.handlers = nil c.index = -1 + c.KeysMutex = &sync.RWMutex{} c.fullPath = "" c.Keys = nil c.Errors = c.Errors[0:0] @@ -219,16 +224,21 @@ func (c *Context) Error(err error) *Error { // Set is used to store a new key/value pair exclusively for this context. // It also lazy initializes c.Keys if it was not used previously. func (c *Context) Set(key string, value interface{}) { + c.KeysMutex.Lock() if c.Keys == nil { c.Keys = make(map[string]interface{}) } + c.Keys[key] = value + c.KeysMutex.Unlock() } // Get returns the value for the given key, ie: (value, true). // If the value does not exists it returns (nil, false) func (c *Context) Get(key string) (value interface{}, exists bool) { + c.KeysMutex.RLock() value, exists = c.Keys[key] + c.KeysMutex.RUnlock() return } diff --git a/gin.go b/gin.go index ab1d0a46..1c2acbc8 100644 --- a/gin.go +++ b/gin.go @@ -162,7 +162,7 @@ func Default() *Engine { } func (engine *Engine) allocateContext() *Context { - return &Context{engine: engine} + return &Context{engine: engine, KeysMutex: &sync.RWMutex{}} } // Delims sets template left and right delims and returns a Engine instance. From c4fd2489ced13e86c6e9328e7d66cd3bb2957f00 Mon Sep 17 00:00:00 2001 From: "Igor H. Vieira" Date: Sat, 21 Mar 2020 23:25:35 -0300 Subject: [PATCH 65/90] =?UTF-8?q?Improved=20the=20graceful=20shutdown=20an?= =?UTF-8?q?d=20restart=20section=20and=20removed=E2=80=A6=20(#2288)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 709a15bc..998783a3 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [Custom HTTP configuration](#custom-http-configuration) - [Support Let's Encrypt](#support-lets-encrypt) - [Run multiple service using Gin](#run-multiple-service-using-gin) - - [Graceful restart or stop](#graceful-restart-or-stop) + - [Graceful shutdown or restart](#graceful-shutdown-or-restart) - [Build a single binary with templates](#build-a-single-binary-with-templates) - [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct) - [Try to bind body into different structs](#try-to-bind-body-into-different-structs) @@ -1687,12 +1687,13 @@ func main() { } ``` -### Graceful restart or stop +### Graceful shutdown or restart -Do you want to graceful restart or stop your web server? -There are some ways this can be done. +There are a few approaches you can use to perform a graceful shutdown or restart. You can make use of third-party packages specifically built for that, or you can manually do the same with the functions and methods from the built-in packages. -We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details. +#### Third-party packages + +We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer to issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details. ```go router := gin.Default() @@ -1701,13 +1702,15 @@ router.GET("/", handler) endless.ListenAndServe(":4242", router) ``` -An alternative to endless: +Alternatives: * [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. * [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server. * [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy for Go servers. -If you are using Go 1.8, you may not need to use this library! Consider using http.Server's built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. See the full [graceful-shutdown](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown) example with gin. +#### Manually + +In case you are using Go 1.8 or a later version, you may not need to use those libraries. Consider using `http.Server`'s built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. The example below describes its usage, and we've got more examples using gin [here](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown). ```go // +build go1.8 @@ -1738,8 +1741,9 @@ func main() { Handler: router, } + // Initializing the server in a goroutine so that + // it won't block the graceful shutdown handling below go func() { - // service connections if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("listen: %s\n", err) } @@ -1753,18 +1757,16 @@ func main() { // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit - log.Println("Shutdown Server ...") + log.Println("Shuting down server...") + // The context is used to inform the server it has 5 seconds to finish + // the request it is currently handling ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { - log.Fatal("Server Shutdown:", err) - } - // catching ctx.Done(). timeout of 5 seconds. - select { - case <-ctx.Done(): - log.Println("timeout of 5 seconds.") + log.Fatal("Server forced to shutdown:", err) } + log.Println("Server exiting") } ``` From a412209e60c4b9562bd723e9e01019829e357a39 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 22 Mar 2020 12:28:46 +0800 Subject: [PATCH 66/90] docs: Add 1.6 changelogs (#2290) --- CHANGELOG.md | 76 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ceb919c..0fb00fa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,54 @@ -### Gin v1.5.0 +## Gin v1.6.0 (Mar 22, 2020) + +### BREAKING + * chore(performance): Improve performance for adding RemoveExtraSlash flag (#2159) + * drop support govendor (#2148) + * Added support for SameSite cookie flag (#1615) +### FEATURES + * add yaml negotitation (#2220) + * FileFromFS (#2112) +### BUGFIXES + * Unix Socket Handling (#2280) + * Use json marshall in context json to fix breaking new line issue. Fixes #2209 (#2228) + * fix accept incoming network connections (#2216) + * Fixed a bug in the calculation of the maximum number of parameters (#2166) + * [FIX] allow empty headers on DataFromReader (#2121) + * Add mutex for protect Context.Keys map (#1391) +### ENHANCEMENTS + * Add mitigation for log injection (#2277) + * tree: range over nodes values (#2229) + * tree: remove duplicate assignment (#2222) + * chore: upgrade go-isatty and json-iterator/go (#2215) + * path: sync code with httprouter (#2212) + * Use zero-copy approach to convert types between string and byte slice (#2206) + * Reuse bytes when cleaning the URL paths (#2179) + * tree: remove one else statement (#2177) + * tree: sync httprouter update (#2173) (#2172) (#2171) + * tree: sync part httprouter codes and reduce if/else (#2163) + * use http method constant (#2155) + * upgrade go-validator to v10 (#2149) + * Refactor redirect request in gin.go (#1970) + * Add build tag nomsgpack (#1852) +### DOCS + * docs(path): improve comments (#2223) + * Renew README to fit the modification of SetCookie method (#2217) + * Fix spelling (#2202) + * Remove broken link from README. (#2198) + * Update docs on Context.Done(), Context.Deadline() and Context.Err() (#2196) + * Update validator to v10 (#2190) + * upgrade go-validator to v10 for README (#2189) + * Update to currently output (#2188) + * Fix "Custom Validators" example (#2186) + * Add project to README (#2165) + * docs(benchmarks): for gin v1.5 (#2153) + * Changed wording for clarity in README.md (#2122) +### MISC + * ci support go1.14 (#2262) + * chore: upgrade depend version (#2231) + * Drop support go1.10 (#2147) + * fix comment in `mode.go` (#2129) + +## Gin v1.5.0 - [FIX] Use DefaultWriter and DefaultErrorWriter for debug messages [#1891](https://github.com/gin-gonic/gin/pull/1891) - [NEW] Now you can parse the inline lowercase start structure [#1893](https://github.com/gin-gonic/gin/pull/1893) @@ -90,7 +140,7 @@ - [NEW] Upgrade dependency libraries [#1491](https://github.com/gin-gonic/gin/pull/1491) -### Gin v1.3.0 +## Gin v1.3.0 - [NEW] Add [`func (*Context) QueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.QueryMap), [`func (*Context) GetQueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetQueryMap), [`func (*Context) PostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.PostFormMap) and [`func (*Context) GetPostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetPostFormMap) to support `type map[string]string` as query string or form parameters, see [#1383](https://github.com/gin-gonic/gin/pull/1383) - [NEW] Add [`func (*Context) AsciiJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.AsciiJSON), see [#1358](https://github.com/gin-gonic/gin/pull/1358) @@ -112,7 +162,7 @@ - [FIX] Gin Mode `""` when calling [`func Mode`](https://godoc.org/github.com/gin-gonic/gin#Mode) now returns `const DebugMode`, see [#1250](https://github.com/gin-gonic/gin/pull/1250) - [FIX] `Flush()` now doesn't overwrite `responseWriter` status code, see [#1460](https://github.com/gin-gonic/gin/pull/1460) -### Gin 1.2.0 +## Gin 1.2.0 - [NEW] Switch from godeps to govendor - [NEW] Add support for Let's Encrypt via gin-gonic/autotls @@ -135,15 +185,15 @@ - [FIX] Use X-Forwarded-For before X-Real-Ip - [FIX] time.Time binding (#904) -### Gin 1.1.4 +## Gin 1.1.4 - [NEW] Support google appengine for IsTerminal func -### Gin 1.1.3 +## Gin 1.1.3 - [FIX] Reverted Logger: skip ANSI color commands -### Gin 1.1 +## Gin 1.1 - [NEW] Implement QueryArray and PostArray methods - [NEW] Refactor GetQuery and GetPostForm @@ -153,7 +203,7 @@ - [FIX] Changed imports to gopkg instead of github in README (#733) - [FIX] Logger: skip ANSI color commands if output is not a tty -### Gin 1.0rc2 (...) +## Gin 1.0rc2 (...) - [PERFORMANCE] Fast path for writing Content-Type. - [PERFORMANCE] Much faster 404 routing @@ -188,7 +238,7 @@ - [FIX] MIT license in every file -### Gin 1.0rc1 (May 22, 2015) +## Gin 1.0rc1 (May 22, 2015) - [PERFORMANCE] Zero allocation router - [PERFORMANCE] Faster JSON, XML and text rendering @@ -232,7 +282,7 @@ - [FIX] Better support for Google App Engine (using log instead of fmt) -### Gin 0.6 (Mar 9, 2015) +## Gin 0.6 (Mar 9, 2015) - [NEW] Support multipart/form-data - [NEW] NoMethod handler @@ -242,14 +292,14 @@ - [FIX] Improve color logger -### Gin 0.5 (Feb 7, 2015) +## Gin 0.5 (Feb 7, 2015) - [NEW] Content Negotiation - [FIX] Solved security bug that allow a client to spoof ip - [FIX] Fix unexported/ignored fields in binding -### Gin 0.4 (Aug 21, 2014) +## Gin 0.4 (Aug 21, 2014) - [NEW] Development mode - [NEW] Unit tests @@ -258,7 +308,7 @@ - [FIX] Improved documentation for model binding -### Gin 0.3 (Jul 18, 2014) +## Gin 0.3 (Jul 18, 2014) - [PERFORMANCE] Normal log and error log are printed in the same call. - [PERFORMANCE] Improve performance of NoRouter() @@ -276,7 +326,7 @@ - [FIX] Check application/x-www-form-urlencoded when parsing form -### Gin 0.2b (Jul 08, 2014) +## Gin 0.2b (Jul 08, 2014) - [PERFORMANCE] Using sync.Pool to allocatio/gc overhead - [NEW] Travis CI integration - [NEW] Completely new logger From ae888314485066c70f31d6d06f3ff6afa635cb12 Mon Sep 17 00:00:00 2001 From: Henry Kwan Date: Mon, 23 Mar 2020 13:52:28 +0800 Subject: [PATCH 67/90] Update version.go (#2293) to sync with tag v1.6.0 --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 6f8235f9..b024c508 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.5.0" +const Version = "v1.6.0" From 1bebd9af9119315dab3870a1011369a11c886a2a Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 23 Mar 2020 17:48:25 +0800 Subject: [PATCH 68/90] Revert "fix accept incoming network connections (#2216)" (#2294) This reverts commit 2ff2b19e14420de970264012e1bcdf91929725b4. --- utils.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils.go b/utils.go index 77b5f0c8..71b80de7 100644 --- a/utils.go +++ b/utils.go @@ -139,10 +139,10 @@ func resolveAddress(addr []string) string { case 0: if port := os.Getenv("PORT"); port != "" { debugPrint("Environment variable PORT=\"%s\"", port) - return "localhost:" + port + return ":" + port } debugPrint("Environment variable PORT is undefined. Using port :8080 by default") - return "localhost:8080" + return ":8080" case 1: return addr[0] default: From 07a6818d24f9b0e3c97b6c44e19af877003bad46 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 23 Mar 2020 18:00:58 +0800 Subject: [PATCH 69/90] bump to v1.6.1 version (#2295) Co-authored-by: thinkerou --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index b024c508..ce6203c6 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.6.0" +const Version = "v1.6.1" From bd5ee1aae2e34e45c7401c3b11c70ac8feac7b41 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Tue, 24 Mar 2020 22:49:34 +0800 Subject: [PATCH 70/90] doc: add pr link (#2298) * doc: add pr link * doc: add v1.6.1 release note --- CHANGELOG.md | 89 +++++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fb00fa4..02459d51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,52 +1,57 @@ -## Gin v1.6.0 (Mar 22, 2020) +## Gin v1.6.1 + +### BUFIXES + * Revert "fix accept incoming network connections" [#2294](https://github.com/gin-gonic/gin/pull/2294) + +## Gin v1.6.0 ### BREAKING - * chore(performance): Improve performance for adding RemoveExtraSlash flag (#2159) - * drop support govendor (#2148) - * Added support for SameSite cookie flag (#1615) + * chore(performance): Improve performance for adding RemoveExtraSlash flag [#2159](https://github.com/gin-gonic/gin/pull/2159) + * drop support govendor [#2148](https://github.com/gin-gonic/gin/pull/2148) + * Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615) ### FEATURES - * add yaml negotitation (#2220) - * FileFromFS (#2112) + * add yaml negotitation [#2220](https://github.com/gin-gonic/gin/pull/2220) + * FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112) ### BUGFIXES - * Unix Socket Handling (#2280) - * Use json marshall in context json to fix breaking new line issue. Fixes #2209 (#2228) - * fix accept incoming network connections (#2216) - * Fixed a bug in the calculation of the maximum number of parameters (#2166) - * [FIX] allow empty headers on DataFromReader (#2121) - * Add mutex for protect Context.Keys map (#1391) + * Unix Socket Handling [#2280](https://github.com/gin-gonic/gin/pull/2280) + * Use json marshall in context json to fix breaking new line issue. Fixes #2209 [#2228](https://github.com/gin-gonic/gin/pull/2228) + * fix accept incoming network connections [#2216](https://github.com/gin-gonic/gin/pull/2216) + * Fixed a bug in the calculation of the maximum number of parameters [#2166](https://github.com/gin-gonic/gin/pull/2166) + * [FIX] allow empty headers on DataFromReader [#2121](https://github.com/gin-gonic/gin/pull/2121) + * Add mutex for protect Context.Keys map [#1391](https://github.com/gin-gonic/gin/pull/1391) ### ENHANCEMENTS - * Add mitigation for log injection (#2277) - * tree: range over nodes values (#2229) - * tree: remove duplicate assignment (#2222) - * chore: upgrade go-isatty and json-iterator/go (#2215) - * path: sync code with httprouter (#2212) - * Use zero-copy approach to convert types between string and byte slice (#2206) - * Reuse bytes when cleaning the URL paths (#2179) - * tree: remove one else statement (#2177) - * tree: sync httprouter update (#2173) (#2172) (#2171) - * tree: sync part httprouter codes and reduce if/else (#2163) - * use http method constant (#2155) - * upgrade go-validator to v10 (#2149) - * Refactor redirect request in gin.go (#1970) - * Add build tag nomsgpack (#1852) + * Add mitigation for log injection [#2277](https://github.com/gin-gonic/gin/pull/2277) + * tree: range over nodes values [#2229](https://github.com/gin-gonic/gin/pull/2229) + * tree: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222) + * chore: upgrade go-isatty and json-iterator/go [#2215](https://github.com/gin-gonic/gin/pull/2215) + * path: sync code with httprouter [#2212](https://github.com/gin-gonic/gin/pull/2212) + * Use zero-copy approach to convert types between string and byte slice [#2206](https://github.com/gin-gonic/gin/pull/2206) + * Reuse bytes when cleaning the URL paths [#2179](https://github.com/gin-gonic/gin/pull/2179) + * tree: remove one else statement [#2177](https://github.com/gin-gonic/gin/pull/2177) + * tree: sync httprouter update (#2173) (#2172) [#2171](https://github.com/gin-gonic/gin/pull/2171) + * tree: sync part httprouter codes and reduce if/else [#2163](https://github.com/gin-gonic/gin/pull/2163) + * use http method constant [#2155](https://github.com/gin-gonic/gin/pull/2155) + * upgrade go-validator to v10 [#2149](https://github.com/gin-gonic/gin/pull/2149) + * Refactor redirect request in gin.go [#1970](https://github.com/gin-gonic/gin/pull/1970) + * Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852) ### DOCS - * docs(path): improve comments (#2223) - * Renew README to fit the modification of SetCookie method (#2217) - * Fix spelling (#2202) - * Remove broken link from README. (#2198) - * Update docs on Context.Done(), Context.Deadline() and Context.Err() (#2196) - * Update validator to v10 (#2190) - * upgrade go-validator to v10 for README (#2189) - * Update to currently output (#2188) - * Fix "Custom Validators" example (#2186) - * Add project to README (#2165) - * docs(benchmarks): for gin v1.5 (#2153) - * Changed wording for clarity in README.md (#2122) + * docs(path): improve comments [#2223](https://github.com/gin-gonic/gin/pull/2223) + * Renew README to fit the modification of SetCookie method [#2217](https://github.com/gin-gonic/gin/pull/2217) + * Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202) + * Remove broken link from README. [#2198](https://github.com/gin-gonic/gin/pull/2198) + * Update docs on Context.Done(), Context.Deadline() and Context.Err() [#2196](https://github.com/gin-gonic/gin/pull/2196) + * Update validator to v10 [#2190](https://github.com/gin-gonic/gin/pull/2190) + * upgrade go-validator to v10 for README [#2189](https://github.com/gin-gonic/gin/pull/2189) + * Update to currently output [#2188](https://github.com/gin-gonic/gin/pull/2188) + * Fix "Custom Validators" example [#2186](https://github.com/gin-gonic/gin/pull/2186) + * Add project to README [#2165](https://github.com/gin-gonic/gin/pull/2165) + * docs(benchmarks): for gin v1.5 [#2153](https://github.com/gin-gonic/gin/pull/2153) + * Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122) ### MISC - * ci support go1.14 (#2262) - * chore: upgrade depend version (#2231) - * Drop support go1.10 (#2147) - * fix comment in `mode.go` (#2129) + * ci support go1.14 [#2262](https://github.com/gin-gonic/gin/pull/2262) + * chore: upgrade depend version [#2231](https://github.com/gin-gonic/gin/pull/2231) + * Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147) + * fix comment in `mode.go` [#2129](https://github.com/gin-gonic/gin/pull/2129) ## Gin v1.5.0 From 57f99ca50fd368c9fdc4543e22d6e84b88571ae5 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 27 Mar 2020 10:47:22 +0800 Subject: [PATCH 71/90] Add set samesite in cookie. (#2306) Signed-off-by: Bo-Yi Wu --- context.go | 13 +++++++++++-- context_test.go | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/context.go b/context.go index 572dbb81..5c1fab22 100644 --- a/context.go +++ b/context.go @@ -71,6 +71,10 @@ type Context struct { // formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH, // or PUT body parameters. formCache url.Values + + // SameSite allows a server to define a cookie attribute making it impossible for + // the browser to send this cookie along with cross-site requests. + sameSite http.SameSite } /************************************/ @@ -782,10 +786,15 @@ func (c *Context) GetRawData() ([]byte, error) { return ioutil.ReadAll(c.Request.Body) } +// SetSameSite with cookie +func (c *Context) SetSameSite(samesite http.SameSite) { + c.sameSite = samesite +} + // 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, sameSite http.SameSite, secure, httpOnly bool) { +func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) { if path == "" { path = "/" } @@ -795,7 +804,7 @@ func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, MaxAge: maxAge, Path: path, Domain: domain, - SameSite: sameSite, + SameSite: c.sameSite, Secure: secure, HttpOnly: httpOnly, }) diff --git a/context_test.go b/context_test.go index 78b22c0d..80c7b407 100644 --- a/context_test.go +++ b/context_test.go @@ -602,13 +602,15 @@ func TestContextPostFormMultipart(t *testing.T) { func TestContextSetCookie(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.SetCookie("user", "gin", 1, "/", "localhost", http.SameSiteLaxMode, true, true) + c.SetSameSite(http.SameSiteLaxMode) + c.SetCookie("user", "gin", 1, "/", "localhost", true, true) assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie")) } func TestContextSetCookiePathEmpty(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.SetCookie("user", "gin", 1, "", "localhost", http.SameSiteLaxMode, true, true) + c.SetSameSite(http.SameSiteLaxMode) + c.SetCookie("user", "gin", 1, "", "localhost", true, true) assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie")) } From 298ebca69107001096eaf81c4b4977b14ffa0e6d Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 27 Mar 2020 10:57:36 +0800 Subject: [PATCH 72/90] fix missing initial sync.RWMutex (#2305) * fix missing initial sync.RWMutex Signed-off-by: Bo-Yi Wu * Add unit testing. Signed-off-by: Bo-Yi Wu --- context.go | 8 ++++++++ context_test.go | 13 +++++++++++++ go.sum | 1 + 3 files changed, 22 insertions(+) diff --git a/context.go b/context.go index 5c1fab22..a2384c0e 100644 --- a/context.go +++ b/context.go @@ -228,6 +228,10 @@ func (c *Context) Error(err error) *Error { // Set is used to store a new key/value pair exclusively for this context. // It also lazy initializes c.Keys if it was not used previously. func (c *Context) Set(key string, value interface{}) { + if c.KeysMutex == nil { + c.KeysMutex = &sync.RWMutex{} + } + c.KeysMutex.Lock() if c.Keys == nil { c.Keys = make(map[string]interface{}) @@ -240,6 +244,10 @@ func (c *Context) Set(key string, value interface{}) { // Get returns the value for the given key, ie: (value, true). // If the value does not exists it returns (nil, false) func (c *Context) Get(key string) (value interface{}, exists bool) { + if c.KeysMutex == nil { + c.KeysMutex = &sync.RWMutex{} + } + c.KeysMutex.RLock() value, exists = c.Keys[key] c.KeysMutex.RUnlock() diff --git a/context_test.go b/context_test.go index 80c7b407..ce077bc6 100644 --- a/context_test.go +++ b/context_test.go @@ -1920,3 +1920,16 @@ func TestRaceParamsContextCopy(t *testing.T) { performRequest(router, "GET", "/name2/api") wg.Wait() } + +func TestContextWithKeysMutex(t *testing.T) { + c := &Context{} + c.Set("foo", "bar") + + value, err := c.Get("foo") + assert.Equal(t, "bar", value) + assert.True(t, err) + + value, err = c.Get("foo2") + assert.Nil(t, value) + assert.False(t, err) +} diff --git a/go.sum b/go.sum index d4998155..4c14fb83 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= From 3315353c20ab224d9308db9df19d83383dba539a Mon Sep 17 00:00:00 2001 From: Henry Kwan Date: Fri, 27 Mar 2020 21:39:11 +0800 Subject: [PATCH 73/90] Update version.go (#2307) sync to tag v1.6.2 --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index ce6203c6..883bdad7 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.6.1" +const Version = "v1.6.2" From 4f208887e1231459672a2a9fc1b2aa40486825d4 Mon Sep 17 00:00:00 2001 From: Shilin Wang Date: Wed, 8 Apr 2020 23:31:31 +0800 Subject: [PATCH 74/90] update set cookie example (#2312) fix #2308 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 998783a3..9dde693d 100644 --- a/README.md +++ b/README.md @@ -2057,7 +2057,7 @@ func main() { if err != nil { cookie = "NotSet" - c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", http.SameSiteLaxMode, false, true) + c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true) } fmt.Printf("Cookie value: %s \n", cookie) From a4e947a356add617b5022c31f6dc28e5b78849fe Mon Sep 17 00:00:00 2001 From: bestgopher <84328409@qq.com> Date: Thu, 16 Apr 2020 22:31:58 +0800 Subject: [PATCH 75/90] update:SetMode function (#2321) --- mode.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mode.go b/mode.go index edfc2940..ca3677a9 100644 --- a/mode.go +++ b/mode.go @@ -50,8 +50,12 @@ func init() { // SetMode sets gin mode according to input string. func SetMode(value string) { + if value == "" { + value = DebugMode + } + switch value { - case DebugMode, "": + case DebugMode: ginMode = debugCode case ReleaseMode: ginMode = releaseCode @@ -60,9 +64,7 @@ func SetMode(value string) { default: panic("gin mode unknown: " + value) } - if value == "" { - value = DebugMode - } + modeName = value } From 90fff292d7befc9794679cc13cb179ee94f603b8 Mon Sep 17 00:00:00 2001 From: Johnny Dallas Date: Thu, 16 Apr 2020 21:26:42 -0700 Subject: [PATCH 76/90] fix typo in the PR template and CONTRIBUTING files (#2323) Co-authored-by: Bo-Yi Wu --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- CONTRIBUTING.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8630bc35..e86bc98f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ - 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. + - It should pass all tests in the available continuous integration 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. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 547b777a..98d758ef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,6 @@ - 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. + - It should pass all tests in the available continuous integration 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. From be4ba7d9df52a96c34715a89a75197aeff2f921a Mon Sep 17 00:00:00 2001 From: Qt Date: Mon, 20 Apr 2020 20:07:36 +0800 Subject: [PATCH 77/90] update: CHANGELOG.md add log for version 1.6.2 (#2329) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02459d51..c26b7658 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## Gin v1.6.2 + +### BUFIXES + * fix missing initial sync.RWMutex [#2305](https://github.com/gin-gonic/gin/pull/2305) +### ENHANCEMENTS + * Add set samesite in cookie. [#2306](https://github.com/gin-gonic/gin/pull/2306) + ## Gin v1.6.1 ### BUFIXES From 4427ca4a607802d2cd45268d83a180ef6aa0b5d4 Mon Sep 17 00:00:00 2001 From: Ronald Petty Date: Mon, 27 Apr 2020 18:36:04 -0700 Subject: [PATCH 78/90] Update gin_integration_test.go (#2341) * Update gin_integration_test.go TestUnixSocket fails if you run it twice in a row. This is due to the unix socket file persisting. Added defer to clean up. Whats unclear is the following test TestBadUnixSocket I suspect is just looking for cruft maybe from a prior test or defaults not working, I have not enough background to say. * Update gin_integration_test.go I believe there is some tab issue here, tried to manual overwrite it now. * Update gin_integration_test.go squash you dang spaces!!! --- gin_integration_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gin_integration_test.go b/gin_integration_test.go index f29d1fc1..5f508c70 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -146,15 +146,19 @@ func TestRunWithPort(t *testing.T) { func TestUnixSocket(t *testing.T) { router := New() + unixTestSocket := "/tmp/unix_unit_test" + + defer os.Remove(unixTestSocket) + go func() { router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") }) - assert.NoError(t, router.RunUnix("/tmp/unix_unit_test")) + assert.NoError(t, router.RunUnix(unixTestSocket)) }() // have to wait for the goroutine to start and run the server // otherwise the main thread will complete time.Sleep(5 * time.Millisecond) - c, err := net.Dial("unix", "/tmp/unix_unit_test") + c, err := net.Dial("unix", unixTestSocket) assert.NoError(t, err) fmt.Fprint(c, "GET /example HTTP/1.0\r\n\r\n") From 2c43278080f90f1b3d3fd52784d765290d36d253 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 3 May 2020 20:39:34 +0800 Subject: [PATCH 79/90] chore(performance): Change *sync.RWMutex to sync.RWMutex (#2351) --- context.go | 27 ++++++++++++--------------- gin.go | 2 +- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/context.go b/context.go index a2384c0e..fb7f54e9 100644 --- a/context.go +++ b/context.go @@ -54,7 +54,7 @@ type Context struct { engine *Engine // This mutex protect Keys map - KeysMutex *sync.RWMutex + mu sync.RWMutex // Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} @@ -86,7 +86,7 @@ func (c *Context) reset() { c.Params = c.Params[0:0] c.handlers = nil c.index = -1 - c.KeysMutex = &sync.RWMutex{} + c.fullPath = "" c.Keys = nil c.Errors = c.Errors[0:0] @@ -98,7 +98,12 @@ func (c *Context) reset() { // Copy returns a copy of the current context that can be safely used outside the request's scope. // This has to be used when the context has to be passed to a goroutine. func (c *Context) Copy() *Context { - var cp = *c + cp := Context{ + writermem: c.writermem, + Request: c.Request, + Params: c.Params, + engine: c.engine, + } cp.writermem.ResponseWriter = nil cp.Writer = &cp.writermem cp.index = abortIndex @@ -228,29 +233,21 @@ func (c *Context) Error(err error) *Error { // Set is used to store a new key/value pair exclusively for this context. // It also lazy initializes c.Keys if it was not used previously. func (c *Context) Set(key string, value interface{}) { - if c.KeysMutex == nil { - c.KeysMutex = &sync.RWMutex{} - } - - c.KeysMutex.Lock() + c.mu.Lock() if c.Keys == nil { c.Keys = make(map[string]interface{}) } c.Keys[key] = value - c.KeysMutex.Unlock() + c.mu.Unlock() } // Get returns the value for the given key, ie: (value, true). // If the value does not exists it returns (nil, false) func (c *Context) Get(key string) (value interface{}, exists bool) { - if c.KeysMutex == nil { - c.KeysMutex = &sync.RWMutex{} - } - - c.KeysMutex.RLock() + c.mu.RLock() value, exists = c.Keys[key] - c.KeysMutex.RUnlock() + c.mu.RUnlock() return } diff --git a/gin.go b/gin.go index 1c2acbc8..ab1d0a46 100644 --- a/gin.go +++ b/gin.go @@ -162,7 +162,7 @@ func Default() *Engine { } func (engine *Engine) allocateContext() *Context { - return &Context{engine: engine, KeysMutex: &sync.RWMutex{}} + return &Context{engine: engine} } // Delims sets template left and right delims and returns a Engine instance. From abc4fa07189cb1f2ed23ec0d3aeac0d34d6348ff Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 3 May 2020 21:12:07 +0800 Subject: [PATCH 80/90] Release v1.6.3 version (#2353) * chore: update version to v1.6.3 * update changelog Signed-off-by: Bo-Yi Wu --- CHANGELOG.md | 8 ++++++++ version.go | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c26b7658..592c2abc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# Gin ChangeLog + +## Gin v1.6.3 + +### ENHANCEMENTS + + * Improve performance: Change `*sync.RWMutex` to `sync.RWMutex` in context. [#2351](https://github.com/gin-gonic/gin/pull/2351) + ## Gin v1.6.2 ### BUFIXES diff --git a/version.go b/version.go index 883bdad7..3e9687dc 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.6.2" +const Version = "v1.6.3" From 54175dbe72289b7524b53a0afd2e5bddfa7b6efc Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 4 May 2020 11:40:41 +0800 Subject: [PATCH 81/90] chore: update the result of CR (#2354) * chore: update the result of CR * Update utils.go * Update utils.go * Update context.go * Update context.go --- auth.go | 5 +++-- context.go | 4 +++- gin.go | 7 ++++--- mode.go | 1 + path.go | 7 ++++--- recovery.go | 2 +- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/auth.go b/auth.go index 9e5d4cf6..43ad36f5 100644 --- a/auth.go +++ b/auth.go @@ -70,8 +70,9 @@ func BasicAuth(accounts Accounts) HandlerFunc { } func processAccounts(accounts Accounts) authPairs { - assert1(len(accounts) > 0, "Empty list of authorized credentials") - pairs := make(authPairs, 0, len(accounts)) + length := len(accounts) + assert1(length > 0, "Empty list of authorized credentials") + pairs := make(authPairs, 0, length) for user, password := range accounts { assert1(user != "", "User can not be empty") value := authorizationHeader(user, password) diff --git a/context.go b/context.go index fb7f54e9..01cff1ae 100644 --- a/context.go +++ b/context.go @@ -34,9 +34,11 @@ const ( MIMEPOSTForm = binding.MIMEPOSTForm MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm MIMEYAML = binding.MIMEYAML - BodyBytesKey = "_gin-gonic/gin/bodybyteskey" ) +// BodyBytesKey indicates a default body bytes key. +const BodyBytesKey = "_gin-gonic/gin/bodybyteskey" + const abortIndex int8 = math.MaxInt8 / 2 // Context is the most important part of gin. It allows us to pass variables between middleware, diff --git a/gin.go b/gin.go index ab1d0a46..888be36b 100644 --- a/gin.go +++ b/gin.go @@ -20,11 +20,12 @@ import ( const defaultMultipartMemory = 32 << 20 // 32 MB var ( - default404Body = []byte("404 page not found") - default405Body = []byte("405 method not allowed") - defaultAppEngine bool + default404Body = []byte("404 page not found") + default405Body = []byte("405 method not allowed") ) +var defaultAppEngine bool + // HandlerFunc defines the handler used by gin middleware as return value. type HandlerFunc func(*Context) diff --git a/mode.go b/mode.go index ca3677a9..11f833e9 100644 --- a/mode.go +++ b/mode.go @@ -22,6 +22,7 @@ const ( // TestMode indicates gin mode is test. TestMode = "test" ) + const ( debugCode = iota releaseCode diff --git a/path.go b/path.go index 51346e4a..d42d6b9d 100644 --- a/path.go +++ b/path.go @@ -136,10 +136,11 @@ func bufApp(buf *[]byte, s string, w int, c byte) { // Otherwise use either the stack buffer, if it is large enough, or // allocate a new buffer on the heap, and copy all previous characters. - if l := len(s); l > cap(b) { - *buf = make([]byte, len(s)) + length := len(s) + if length > cap(b) { + *buf = make([]byte, length) } else { - *buf = (*buf)[:l] + *buf = (*buf)[:length] } b = *buf diff --git a/recovery.go b/recovery.go index bc946c03..8cf0932a 100644 --- a/recovery.go +++ b/recovery.go @@ -146,6 +146,6 @@ func function(pc uintptr) []byte { } func timeFormat(t time.Time) string { - var timeString = t.Format("2006/01/02 - 15:04:05") + timeString := t.Format("2006/01/02 - 15:04:05") return timeString } From 6ac7f194c4e4867f30c94674af3015fceb1a97af Mon Sep 17 00:00:00 2001 From: thinkerou Date: Tue, 5 May 2020 13:55:57 +0800 Subject: [PATCH 82/90] chore: update some code style (#2356) --- context.go | 2 +- gin.go | 8 ++++---- githubapi_test.go | 8 ++++---- internal/json/jsoniter.go | 2 +- routes_test.go | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/context.go b/context.go index 01cff1ae..4ebcc294 100644 --- a/context.go +++ b/context.go @@ -865,7 +865,7 @@ func (c *Context) IndentedJSON(code int, obj interface{}) { // 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}) + c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj}) } // JSONP serializes the given struct as JSON into the response body. diff --git a/gin.go b/gin.go index 888be36b..4e72f81d 100644 --- a/gin.go +++ b/gin.go @@ -104,7 +104,7 @@ type Engine struct { RemoveExtraSlash bool delims render.Delims - secureJsonPrefix string + secureJSONPrefix string HTMLRender render.HTMLRender FuncMap template.FuncMap allNoRoute HandlersChain @@ -145,7 +145,7 @@ func New() *Engine { MaxMultipartMemory: defaultMultipartMemory, trees: make(methodTrees, 0, 9), delims: render.Delims{Left: "{{", Right: "}}"}, - secureJsonPrefix: "while(1);", + secureJSONPrefix: "while(1);", } engine.RouterGroup.engine = engine engine.pool.New = func() interface{} { @@ -172,9 +172,9 @@ func (engine *Engine) Delims(left, right string) *Engine { return engine } -// SecureJsonPrefix sets the secureJsonPrefix used in Context.SecureJSON. +// SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON. func (engine *Engine) SecureJsonPrefix(prefix string) *Engine { - engine.secureJsonPrefix = prefix + engine.secureJSONPrefix = prefix return engine } diff --git a/githubapi_test.go b/githubapi_test.go index 925c5a14..3f440bce 100644 --- a/githubapi_test.go +++ b/githubapi_test.go @@ -291,13 +291,13 @@ func TestShouldBindUri(t *testing.T) { type Person struct { Name string `uri:"name" binding:"required"` - Id string `uri:"id" binding:"required"` + ID string `uri:"id" binding:"required"` } router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { var person Person assert.NoError(t, c.ShouldBindUri(&person)) assert.True(t, "" != person.Name) - assert.True(t, "" != person.Id) + assert.True(t, "" != person.ID) c.String(http.StatusOK, "ShouldBindUri test OK") }) @@ -313,13 +313,13 @@ func TestBindUri(t *testing.T) { type Person struct { Name string `uri:"name" binding:"required"` - Id string `uri:"id" binding:"required"` + ID string `uri:"id" binding:"required"` } router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { var person Person assert.NoError(t, c.BindUri(&person)) assert.True(t, "" != person.Name) - assert.True(t, "" != person.Id) + assert.True(t, "" != person.ID) c.String(http.StatusOK, "BindUri test OK") }) diff --git a/internal/json/jsoniter.go b/internal/json/jsoniter.go index fabd7b84..649a3cdb 100644 --- a/internal/json/jsoniter.go +++ b/internal/json/jsoniter.go @@ -6,7 +6,7 @@ package json -import "github.com/json-iterator/go" +import jsoniter "github.com/json-iterator/go" var ( json = jsoniter.ConfigCompatibleWithStandardLibrary diff --git a/routes_test.go b/routes_test.go index ee6ea823..360ade62 100644 --- a/routes_test.go +++ b/routes_test.go @@ -514,7 +514,7 @@ func TestMiddlewareCalledOnceByRouterStaticFSNotFound(t *testing.T) { // Middleware must be called just only once by per request. middlewareCalledNum := 0 router.Use(func(c *Context) { - middlewareCalledNum += 1 + middlewareCalledNum++ }) router.StaticFS("/", http.FileSystem(http.Dir("/thisreallydoesntexist/"))) From 747efffd2a4571681ed227ef415945a7e5713bc3 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Tue, 5 May 2020 14:06:42 +0800 Subject: [PATCH 83/90] chore: update godoc address (#2357) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9dde693d..dae63e24 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![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) [![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin) -[![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://godoc.org/github.com/gin-gonic/gin) +[![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc) [![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge) [![Open Source Helpers](https://www.codetriage.com/gin-gonic/gin/badges/users.svg)](https://www.codetriage.com/gin-gonic/gin) From 05464a8f6b8068d488660dd2b0c767e95810156f Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 5 May 2020 16:37:40 +0800 Subject: [PATCH 84/90] docs: update benchmark result v1.6.3 (#2355) --- BENCHMARKS.md | 1267 ++++++++++++++++++++++++------------------------- README.md | 64 +-- 2 files changed, 655 insertions(+), 676 deletions(-) diff --git a/BENCHMARKS.md b/BENCHMARKS.md index e4ff4677..b164ae06 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -1,693 +1,666 @@ -## Benchmark System +# Benchmark System -**VM HOST:** DigitalOcean -**Machine:** 12 CPU, 24 GB RAM. Ubuntu 16.04.2 x64 -**Date:** Nov 26th, 2019 -**Go Version:** 1.13.4 linux/amd64 -**Source:** [Go HTTP Router Benchmark](https://github.com/julienschmidt/go-http-routing-benchmark) -**Result:** [See the gist](https://gist.github.com/appleboy/b5f2ecfaf50824ae9c64dcfb9165ae5e) +**VM HOST:** Travis +**Machine:** Ubuntu 16.04.6 LTS x64 +**Date:** May 04th, 2020 +**Version:** Gin v1.6.3 +**Go Version:** 1.14.2 linux/amd64 +**Source:** [Go HTTP Router Benchmark](https://github/gin-gonic/go-http-routing-benchmark) +**Result:** [See the gist](https://gist.github.com/appleboy/b5f2ecfaf50824ae9c64dcfb9165ae5e) or [Travis result](https://travis-ci.org/github/gin-gonic/go-http-routing-benchmark/jobs/682947061) ## Static Routes: 157 -``` -Gin: 34936 Bytes +```sh +Gin: 34936 Bytes -HttpServeMux: 14512 Bytes -Ace: 30648 Bytes -Aero: 800696 Bytes -Bear: 30664 Bytes -Beego: 98456 Bytes -Bone: 40224 Bytes -Chi: 83608 Bytes -CloudyKitRouter: 30448 Bytes -Denco: 9928 Bytes -Echo: 76584 Bytes -GocraftWeb: 55496 Bytes -Goji: 29744 Bytes -Gojiv2: 105840 Bytes -GoJsonRest: 137512 Bytes -GoRestful: 816936 Bytes -GorillaMux: 585632 Bytes -GowwwRouter: 24968 Bytes -HttpRouter: 21680 Bytes -HttpTreeMux: 73448 Bytes -Kocha: 115472 Bytes -LARS: 30640 Bytes -Macaron: 38592 Bytes -Martini: 310864 Bytes -Pat: 19696 Bytes -Possum: 89920 Bytes -R2router: 23712 Bytes -Rivet: 24608 Bytes -Tango: 28264 Bytes -TigerTonic: 78768 Bytes -Traffic: 538976 Bytes -Vulcan: 369960 Bytes +HttpServeMux: 14512 Bytes +Ace: 30680 Bytes +Aero: 34536 Bytes +Bear: 30456 Bytes +Beego: 98456 Bytes +Bone: 40224 Bytes +Chi: 83608 Bytes +Denco: 10216 Bytes +Echo: 80328 Bytes +GocraftWeb: 55288 Bytes +Goji: 29744 Bytes +Gojiv2: 105840 Bytes +GoJsonRest: 137496 Bytes +GoRestful: 816936 Bytes +GorillaMux: 585632 Bytes +GowwwRouter: 24968 Bytes +HttpRouter: 21712 Bytes +HttpTreeMux: 73448 Bytes +Kocha: 115472 Bytes +LARS: 30640 Bytes +Macaron: 38592 Bytes +Martini: 310864 Bytes +Pat: 19696 Bytes +Possum: 89920 Bytes +R2router: 23712 Bytes +Rivet: 24608 Bytes +Tango: 28264 Bytes +TigerTonic: 78768 Bytes +Traffic: 538976 Bytes +Vulcan: 369960 Bytes ``` ## GithubAPI Routes: 203 -``` -Gin: 58512 Bytes +```sh +Gin: 58512 Bytes -Ace: 48640 Bytes -Aero: 1386208 Bytes -Bear: 82536 Bytes -Beego: 150936 Bytes -Bone: 100976 Bytes -Chi: 95112 Bytes -CloudyKitRouter: 93704 Bytes -Denco: 36736 Bytes -Echo: 96328 Bytes -GocraftWeb: 95432 Bytes -Goji: 51600 Bytes -Gojiv2: 104704 Bytes -GoJsonRest: 142024 Bytes -GoRestful: 1241656 Bytes -GorillaMux: 1322784 Bytes -GowwwRouter: 80008 Bytes -HttpRouter: 37096 Bytes -HttpTreeMux: 78800 Bytes -Kocha: 785408 Bytes -LARS: 48600 Bytes -Macaron: 93680 Bytes -Martini: 485264 Bytes -Pat: 21200 Bytes -Possum: 85312 Bytes -R2router: 47104 Bytes -Rivet: 42840 Bytes -Tango: 54840 Bytes -TigerTonic: 96176 Bytes -Traffic: 921744 Bytes -Vulcan: 425368 Bytes +Ace: 48688 Bytes +Aero: 318568 Bytes +Bear: 84248 Bytes +Beego: 150936 Bytes +Bone: 100976 Bytes +Chi: 95112 Bytes +Denco: 36736 Bytes +Echo: 100296 Bytes +GocraftWeb: 95432 Bytes +Goji: 49680 Bytes +Gojiv2: 104704 Bytes +GoJsonRest: 141976 Bytes +GoRestful: 1241656 Bytes +GorillaMux: 1322784 Bytes +GowwwRouter: 80008 Bytes +HttpRouter: 37144 Bytes +HttpTreeMux: 78800 Bytes +Kocha: 785120 Bytes +LARS: 48600 Bytes +Macaron: 92784 Bytes +Martini: 485264 Bytes +Pat: 21200 Bytes +Possum: 85312 Bytes +R2router: 47104 Bytes +Rivet: 42840 Bytes +Tango: 54840 Bytes +TigerTonic: 95264 Bytes +Traffic: 921744 Bytes +Vulcan: 425992 Bytes ``` ## GPlusAPI Routes: 13 -``` -Gin: 4384 Bytes +```sh +Gin: 4384 Bytes -Ace: 3 664 Bytes -Aero: 88248 Bytes -Bear: 7112 Bytes -Beego: 10272 Bytes -Bone: 6688 Bytes -Chi: 8024 Bytes -CloudyKitRouter: 6728 Bytes -Denco: 3264 Bytes -Echo: 9272 Bytes -GocraftWeb: 7496 Bytes -Goji: 3152 Bytes -Gojiv2: 7376 Bytes -GoJsonRest: 11416 Bytes -GoRestful: 74328 Bytes -GorillaMux: 66208 Bytes -GowwwRouter: 5744 Bytes -HttpRouter: 2760 Bytes -HttpTreeMux: 7440 Bytes -Kocha: 128880 Bytes -LARS: 3656 Bytes -Macaron: 8656 Bytes -Martini: 23920 Bytes -Pat: 1856 Bytes -Possum: 7248 Bytes -R2router: 3928 Bytes -Rivet: 3064 Bytes -Tango: 5168 Bytes -TigerTonic: 9408 Bytes -Traffic: 46400 Bytes -Vulcan: 25544 Bytes +Ace: 3712 Bytes +Aero: 26056 Bytes +Bear: 7112 Bytes +Beego: 10272 Bytes +Bone: 6688 Bytes +Chi: 8024 Bytes +Denco: 3264 Bytes +Echo: 9688 Bytes +GocraftWeb: 7496 Bytes +Goji: 3152 Bytes +Gojiv2: 7376 Bytes +GoJsonRest: 11400 Bytes +GoRestful: 74328 Bytes +GorillaMux: 66208 Bytes +GowwwRouter: 5744 Bytes +HttpRouter: 2808 Bytes +HttpTreeMux: 7440 Bytes +Kocha: 128880 Bytes +LARS: 3656 Bytes +Macaron: 8656 Bytes +Martini: 23920 Bytes +Pat: 1856 Bytes +Possum: 7248 Bytes +R2router: 3928 Bytes +Rivet: 3064 Bytes +Tango: 5168 Bytes +TigerTonic: 9408 Bytes +Traffic: 46400 Bytes +Vulcan: 25544 Bytes ``` ## ParseAPI Routes: 26 -``` -Gin: 7776 Bytes +```sh +Gin: 7776 Bytes -Ace: 6656 Bytes -Aero: 163736 Bytes -Bear: 12528 Bytes -Beego: 19280 Bytes -Bone: 11440 Bytes -Chi: 9744 Bytes -Denco: 4192 Bytes -Echo: 11648 Bytes -GocraftWeb: 12800 Bytes -Goji: 5680 Bytes -Gojiv2: 14464 Bytes -GoJsonRest: 14424 Bytes -GoRestful: 116264 Bytes -GorillaMux: 105880 Bytes -GowwwRouter: 9344 Bytes -HttpRouter: 5024 Bytes -HttpTreeMux: 7848 Bytes -Kocha: 181712 Bytes -LARS: 6632 Bytes -Macaron: 13648 Bytes -Martini: 45888 Bytes -Pat: 2560 Bytes -Possum: 9200 Bytes -R2router: 7056 Bytes -Rivet: 5680 Bytes -Tango: 8920 Bytes -TigerTonic: 9840 Bytes -Traffic: 79096 Bytes -Vulcan: 44504 Bytes +Ace: 6704 Bytes +Aero: 28488 Bytes +Bear: 12320 Bytes +Beego: 19280 Bytes +Bone: 11440 Bytes +Chi: 9744 Bytes +Denco: 4192 Bytes +Echo: 11664 Bytes +GocraftWeb: 12800 Bytes +Goji: 5680 Bytes +Gojiv2: 14464 Bytes +GoJsonRest: 14072 Bytes +GoRestful: 116264 Bytes +GorillaMux: 105880 Bytes +GowwwRouter: 9344 Bytes +HttpRouter: 5072 Bytes +HttpTreeMux: 7848 Bytes +Kocha: 181712 Bytes +LARS: 6632 Bytes +Macaron: 13648 Bytes +Martini: 45888 Bytes +Pat: 2560 Bytes +Possum: 9200 Bytes +R2router: 7056 Bytes +Rivet: 5680 Bytes +Tango: 8920 Bytes +TigerTonic: 9840 Bytes +Traffic: 79096 Bytes +Vulcan: 44504 Bytes ``` ## Static Routes -``` -BenchmarkGin_StaticAll 25604 45487 ns/op 0 B/op 0 allocs/op +```sh +BenchmarkGin_StaticAll 62169 19319 ns/op 0 B/op 0 allocs/op -BenchmarkAce_StaticAll 28402 42046 ns/op 0 B/op 0 allocs/op -BenchmarkAero_StaticAll 38766 30333 ns/op 0 B/op 0 allocs/op -BenchmarkHttpServeMux_StaticAll 25728 46511 ns/op 0 B/op 0 allocs/op -BenchmarkBeego_StaticAll 5098 288527 ns/op 55264 B/op 471 allocs/op -BenchmarkBear_StaticAll 10000 126323 ns/op 20272 B/op 469 allocs/op -BenchmarkBone_StaticAll 9499 113631 ns/op 0 B/op 0 allocs/op -BenchmarkChi_StaticAll 7912 237363 ns/op 67824 B/op 471 allocs/op -BenchmarkCloudyKitRouter_StaticAll 41626 28668 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_StaticAll 95774 12221 ns/op 0 B/op 0 allocs/op -BenchmarkEcho_StaticAll 26246 44603 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_StaticAll 10000 193337 ns/op 46312 B/op 785 allocs/op -BenchmarkGoji_StaticAll 15886 75789 ns/op 0 B/op 0 allocs/op -BenchmarkGojiv2_StaticAll 1886 597374 ns/op 205984 B/op 1570 allocs/op -BenchmarkGoJsonRest_StaticAll 4700 307144 ns/op 51653 B/op 1727 allocs/op -BenchmarkGoRestful_StaticAll 429 2880165 ns/op 613280 B/op 2053 allocs/op -BenchmarkGorillaMux_StaticAll 754 1491761 ns/op 153233 B/op 1413 allocs/op -BenchmarkGowwwRouter_StaticAll 28071 42629 ns/op 0 B/op 0 allocs/op -BenchmarkHttpRouter_StaticAll 47672 24875 ns/op 0 B/op 0 allocs/op -BenchmarkHttpTreeMux_StaticAll 46770 25100 ns/op 0 B/op 0 allocs/op -BenchmarkKocha_StaticAll 61045 19494 ns/op 0 B/op 0 allocs/op -BenchmarkLARS_StaticAll 36103 32700 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_StaticAll 4261 430131 ns/op 115552 B/op 1256 allocs/op -BenchmarkMartini_StaticAll 481 2320157 ns/op 125444 B/op 1717 allocs/op -BenchmarkPat_StaticAll 325 3739521 ns/op 602832 B/op 12559 allocs/op -BenchmarkPossum_StaticAll 10000 203575 ns/op 65312 B/op 471 allocs/op -BenchmarkR2router_StaticAll 10000 110536 ns/op 22608 B/op 628 allocs/op -BenchmarkRivet_StaticAll 23344 51174 ns/op 0 B/op 0 allocs/op -BenchmarkTango_StaticAll 3596 340045 ns/op 39209 B/op 1256 allocs/op -BenchmarkTigerTonic_StaticAll 16784 71807 ns/op 7376 B/op 157 allocs/op -BenchmarkTraffic_StaticAll 350 3435155 ns/op 754862 B/op 14601 allocs/op -BenchmarkVulcan_StaticAll 5930 200284 ns/op 15386 B/op 471 allocs/op +BenchmarkAce_StaticAll 65428 18313 ns/op 0 B/op 0 allocs/op +BenchmarkAero_StaticAll 121132 9632 ns/op 0 B/op 0 allocs/op +BenchmarkHttpServeMux_StaticAll 52626 22758 ns/op 0 B/op 0 allocs/op +BenchmarkBeego_StaticAll 9962 179058 ns/op 55264 B/op 471 allocs/op +BenchmarkBear_StaticAll 14894 80966 ns/op 20272 B/op 469 allocs/op +BenchmarkBone_StaticAll 18718 64065 ns/op 0 B/op 0 allocs/op +BenchmarkChi_StaticAll 10000 149827 ns/op 67824 B/op 471 allocs/op +BenchmarkDenco_StaticAll 211393 5680 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_StaticAll 49341 24343 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_StaticAll 10000 126209 ns/op 46312 B/op 785 allocs/op +BenchmarkGoji_StaticAll 27956 43174 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_StaticAll 3430 370718 ns/op 205984 B/op 1570 allocs/op +BenchmarkGoJsonRest_StaticAll 9134 188888 ns/op 51653 B/op 1727 allocs/op +BenchmarkGoRestful_StaticAll 706 1703330 ns/op 613280 B/op 2053 allocs/op +BenchmarkGorillaMux_StaticAll 1268 924083 ns/op 153233 B/op 1413 allocs/op +BenchmarkGowwwRouter_StaticAll 63374 18935 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_StaticAll 109938 10902 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_StaticAll 109166 10861 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_StaticAll 92258 12992 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_StaticAll 65200 18387 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_StaticAll 5671 291501 ns/op 115553 B/op 1256 allocs/op +BenchmarkMartini_StaticAll 807 1460498 ns/op 125444 B/op 1717 allocs/op +BenchmarkPat_StaticAll 513 2342396 ns/op 602832 B/op 12559 allocs/op +BenchmarkPossum_StaticAll 10000 128270 ns/op 65312 B/op 471 allocs/op +BenchmarkR2router_StaticAll 16726 71760 ns/op 22608 B/op 628 allocs/op +BenchmarkRivet_StaticAll 41722 28723 ns/op 0 B/op 0 allocs/op +BenchmarkTango_StaticAll 7606 205082 ns/op 39209 B/op 1256 allocs/op +BenchmarkTigerTonic_StaticAll 26247 45806 ns/op 7376 B/op 157 allocs/op +BenchmarkTraffic_StaticAll 550 2284518 ns/op 754864 B/op 14601 allocs/op +BenchmarkVulcan_StaticAll 10000 131343 ns/op 15386 B/op 471 allocs/op ``` ## Micro Benchmarks -``` -BenchmarkGin_Param 8623915 139 ns/op 0 B/op 0 allocs/op - -BenchmarkAce_Param 3976539 290 ns/op 32 B/op 1 allocs/op -BenchmarkAero_Param 8948976 133 ns/op 0 B/op 0 allocs/op -BenchmarkBear_Param 1000000 1277 ns/op 456 B/op 5 allocs/op -BenchmarkBeego_Param 889404 1785 ns/op 352 B/op 3 allocs/op -BenchmarkBone_Param 1000000 2219 ns/op 816 B/op 6 allocs/op -BenchmarkChi_Param 1000000 1386 ns/op 432 B/op 3 allocs/op -BenchmarkCloudyKitRouter_Param 18343244 61.2 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_Param 5637424 204 ns/op 32 B/op 1 allocs/op -BenchmarkEcho_Param 9540910 122 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_Param 1000000 1939 ns/op 648 B/op 8 allocs/op -BenchmarkGoji_Param 1283509 938 ns/op 336 B/op 2 allocs/op -BenchmarkGojiv2_Param 331266 3554 ns/op 1328 B/op 11 allocs/op -BenchmarkGoJsonRest_Param 908851 2158 ns/op 649 B/op 13 allocs/op -BenchmarkGoRestful_Param 135781 9339 ns/op 4192 B/op 14 allocs/op -BenchmarkGorillaMux_Param 308407 3893 ns/op 1280 B/op 10 allocs/op -BenchmarkGowwwRouter_Param 1000000 1044 ns/op 432 B/op 3 allocs/op -BenchmarkHttpRouter_Param 6653476 162 ns/op 32 B/op 1 allocs/op -BenchmarkHttpTreeMux_Param 1361378 819 ns/op 352 B/op 3 allocs/op -BenchmarkKocha_Param 3084330 353 ns/op 56 B/op 3 allocs/op -BenchmarkLARS_Param 11502079 107 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_Param 439095 3750 ns/op 1072 B/op 10 allocs/op -BenchmarkMartini_Param 177099 7479 ns/op 1072 B/op 10 allocs/op -BenchmarkPat_Param 729747 2048 ns/op 536 B/op 11 allocs/op -BenchmarkPossum_Param 995989 1705 ns/op 496 B/op 5 allocs/op -BenchmarkR2router_Param 1000000 1037 ns/op 432 B/op 5 allocs/op -BenchmarkRivet_Param 4057065 271 ns/op 48 B/op 1 allocs/op -BenchmarkTango_Param 812029 1682 ns/op 248 B/op 8 allocs/op -BenchmarkTigerTonic_Param 450592 3358 ns/op 776 B/op 16 allocs/op -BenchmarkTraffic_Param 206390 5661 ns/op 1856 B/op 21 allocs/op -BenchmarkVulcan_Param 1441147 792 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_Param5 1891473 632 ns/op 160 B/op 1 allocs/op -BenchmarkAero_Param5 5191258 227 ns/op 0 B/op 0 allocs/op -BenchmarkBear_Param5 988882 1734 ns/op 501 B/op 5 allocs/op -BenchmarkBeego_Param5 625438 2132 ns/op 352 B/op 3 allocs/op -BenchmarkBone_Param5 622030 3061 ns/op 864 B/op 6 allocs/op -BenchmarkChi_Param5 1000000 1735 ns/op 432 B/op 3 allocs/op -BenchmarkCloudyKitRouter_Param5 5167868 225 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_Param5 2174550 550 ns/op 160 B/op 1 allocs/op -BenchmarkEcho_Param5 4272258 275 ns/op 0 B/op 0 allocs/op -BenchmarkGin_Param5 4190391 275 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_Param5 623739 3107 ns/op 920 B/op 11 allocs/op -BenchmarkGoji_Param5 1000000 1310 ns/op 336 B/op 2 allocs/op -BenchmarkGojiv2_Param5 314694 3803 ns/op 1392 B/op 11 allocs/op -BenchmarkGoJsonRest_Param5 308203 4108 ns/op 1097 B/op 16 allocs/op -BenchmarkGoRestful_Param5 115048 9787 ns/op 4288 B/op 14 allocs/op -BenchmarkGorillaMux_Param5 180812 5658 ns/op 1344 B/op 10 allocs/op -BenchmarkGowwwRouter_Param5 1000000 1156 ns/op 432 B/op 3 allocs/op -BenchmarkHttpRouter_Param5 2395767 502 ns/op 160 B/op 1 allocs/op -BenchmarkHttpTreeMux_Param5 899263 2096 ns/op 576 B/op 6 allocs/op -BenchmarkKocha_Param5 1000000 1639 ns/op 440 B/op 10 allocs/op -BenchmarkLARS_Param5 5807994 203 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_Param5 272967 4087 ns/op 1072 B/op 10 allocs/op -BenchmarkMartini_Param5 120735 8886 ns/op 1232 B/op 11 allocs/op -BenchmarkPat_Param5 294714 4943 ns/op 888 B/op 29 allocs/op -BenchmarkPossum_Param5 1000000 1689 ns/op 496 B/op 5 allocs/op -BenchmarkR2router_Param5 1000000 1319 ns/op 432 B/op 5 allocs/op -BenchmarkRivet_Param5 1347289 883 ns/op 240 B/op 1 allocs/op -BenchmarkTango_Param5 617077 2091 ns/op 360 B/op 8 allocs/op -BenchmarkTigerTonic_Param5 113659 11212 ns/op 2279 B/op 39 allocs/op -BenchmarkTraffic_Param5 134148 9039 ns/op 2208 B/op 27 allocs/op -BenchmarkVulcan_Param5 1000000 1095 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_Param20 1000000 1838 ns/op 640 B/op 1 allocs/op -BenchmarkAero_Param20 17120668 66.1 ns/op 0 B/op 0 allocs/op -BenchmarkBear_Param20 205585 5332 ns/op 1665 B/op 5 allocs/op -BenchmarkBeego_Param20 230522 5382 ns/op 352 B/op 3 allocs/op -BenchmarkBone_Param20 167190 8076 ns/op 2031 B/op 6 allocs/op -BenchmarkChi_Param20 480528 3044 ns/op 432 B/op 3 allocs/op -BenchmarkCloudyKitRouter_Param20 1347794 872 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_Param20 1000000 1867 ns/op 640 B/op 1 allocs/op -BenchmarkEcho_Param20 1363526 897 ns/op 0 B/op 0 allocs/op -BenchmarkGin_Param20 1607217 748 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_Param20 97314 11671 ns/op 3795 B/op 15 allocs/op -BenchmarkGoji_Param20 289407 4220 ns/op 1246 B/op 2 allocs/op -BenchmarkGojiv2_Param20 245186 4869 ns/op 1632 B/op 11 allocs/op -BenchmarkGoJsonRest_Param20 78049 15725 ns/op 4485 B/op 20 allocs/op -BenchmarkGoRestful_Param20 66907 18031 ns/op 6716 B/op 18 allocs/op -BenchmarkGorillaMux_Param20 81866 12422 ns/op 3452 B/op 12 allocs/op -BenchmarkGowwwRouter_Param20 955983 1688 ns/op 432 B/op 3 allocs/op -BenchmarkHttpRouter_Param20 1000000 1629 ns/op 640 B/op 1 allocs/op -BenchmarkHttpTreeMux_Param20 108940 10241 ns/op 3195 B/op 10 allocs/op -BenchmarkKocha_Param20 197022 5488 ns/op 1808 B/op 27 allocs/op -BenchmarkLARS_Param20 2451241 490 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_Param20 106770 10788 ns/op 2923 B/op 12 allocs/op -BenchmarkMartini_Param20 69028 17112 ns/op 3596 B/op 13 allocs/op -BenchmarkPat_Param20 56275 21535 ns/op 4424 B/op 93 allocs/op -BenchmarkPossum_Param20 1000000 1705 ns/op 496 B/op 5 allocs/op -BenchmarkR2router_Param20 172215 7099 ns/op 2283 B/op 7 allocs/op -BenchmarkRivet_Param20 447265 2987 ns/op 1024 B/op 1 allocs/op -BenchmarkTango_Param20 327494 3850 ns/op 856 B/op 8 allocs/op -BenchmarkTigerTonic_Param20 27176 44571 ns/op 9871 B/op 119 allocs/op -BenchmarkTraffic_Param20 38828 31025 ns/op 7856 B/op 47 allocs/op -BenchmarkVulcan_Param20 560442 1807 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_ParamWrite 2712150 442 ns/op 40 B/op 2 allocs/op -BenchmarkAero_ParamWrite 6392880 189 ns/op 0 B/op 0 allocs/op -BenchmarkBear_ParamWrite 1000000 1338 ns/op 456 B/op 5 allocs/op -BenchmarkBeego_ParamWrite 821431 1886 ns/op 360 B/op 4 allocs/op -BenchmarkBone_ParamWrite 913227 2350 ns/op 816 B/op 6 allocs/op -BenchmarkChi_ParamWrite 1000000 1427 ns/op 432 B/op 3 allocs/op -BenchmarkCloudyKitRouter_ParamWrite 18645724 60.9 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_ParamWrite 4394764 264 ns/op 32 B/op 1 allocs/op -BenchmarkEcho_ParamWrite 5288883 242 ns/op 8 B/op 1 allocs/op -BenchmarkGin_ParamWrite 4584932 253 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_ParamWrite 866242 2094 ns/op 656 B/op 9 allocs/op -BenchmarkGoji_ParamWrite 1201875 1004 ns/op 336 B/op 2 allocs/op -BenchmarkGojiv2_ParamWrite 317766 3777 ns/op 1360 B/op 13 allocs/op -BenchmarkGoJsonRest_ParamWrite 380242 3447 ns/op 1128 B/op 18 allocs/op -BenchmarkGoRestful_ParamWrite 131046 9340 ns/op 4200 B/op 15 allocs/op -BenchmarkGorillaMux_ParamWrite 298428 3970 ns/op 1280 B/op 10 allocs/op -BenchmarkGowwwRouter_ParamWrite 655940 2744 ns/op 976 B/op 8 allocs/op -BenchmarkHttpRouter_ParamWrite 5237014 219 ns/op 32 B/op 1 allocs/op -BenchmarkHttpTreeMux_ParamWrite 1379904 853 ns/op 352 B/op 3 allocs/op -BenchmarkKocha_ParamWrite 2939042 400 ns/op 56 B/op 3 allocs/op -BenchmarkLARS_ParamWrite 6181642 199 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_ParamWrite 352497 4670 ns/op 1176 B/op 14 allocs/op -BenchmarkMartini_ParamWrite 138259 8543 ns/op 1176 B/op 14 allocs/op -BenchmarkPat_ParamWrite 552386 3262 ns/op 960 B/op 15 allocs/op -BenchmarkPossum_ParamWrite 1000000 1711 ns/op 496 B/op 5 allocs/op -BenchmarkR2router_ParamWrite 1000000 1085 ns/op 432 B/op 5 allocs/op -BenchmarkRivet_ParamWrite 2374513 489 ns/op 112 B/op 2 allocs/op -BenchmarkTango_ParamWrite 1443907 812 ns/op 136 B/op 4 allocs/op -BenchmarkTigerTonic_ParamWrite 324264 4874 ns/op 1216 B/op 21 allocs/op -BenchmarkTraffic_ParamWrite 170726 7155 ns/op 2280 B/op 25 allocs/op -BenchmarkVulcan_ParamWrite 1498888 776 ns/op 98 B/op 3 allocs/op +```sh +BenchmarkGin_Param 18785022 63.9 ns/op 0 B/op 0 allocs/op +BenchmarkAce_Param 14689765 81.5 ns/op 0 B/op 0 allocs/op +BenchmarkAero_Param 23094770 51.2 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Param 1417045 845 ns/op 456 B/op 5 allocs/op +BenchmarkBeego_Param 1000000 1080 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Param 1000000 1463 ns/op 816 B/op 6 allocs/op +BenchmarkChi_Param 1378756 885 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_Param 8557899 143 ns/op 32 B/op 1 allocs/op +BenchmarkEcho_Param 16433347 75.5 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param 1000000 1218 ns/op 648 B/op 8 allocs/op +BenchmarkGoji_Param 1921248 617 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Param 561848 2156 ns/op 1328 B/op 11 allocs/op +BenchmarkGoJsonRest_Param 1000000 1358 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_Param 224857 5307 ns/op 4192 B/op 14 allocs/op +BenchmarkGorillaMux_Param 498313 2459 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_Param 1864354 654 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Param 26269074 47.7 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_Param 2109829 557 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_Param 5050216 243 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_Param 19811712 59.9 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param 662746 2329 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_Param 279902 4260 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_Param 1000000 1382 ns/op 536 B/op 11 allocs/op +BenchmarkPossum_Param 1000000 1014 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Param 1712559 707 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Param 6648086 182 ns/op 48 B/op 1 allocs/op +BenchmarkTango_Param 1221504 994 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_Param 891661 2261 ns/op 776 B/op 16 allocs/op +BenchmarkTraffic_Param 350059 3598 ns/op 1856 B/op 21 allocs/op +BenchmarkVulcan_Param 2517823 472 ns/op 98 B/op 3 allocs/op +BenchmarkAce_Param5 9214365 130 ns/op 0 B/op 0 allocs/op +BenchmarkAero_Param5 15369013 77.9 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Param5 1000000 1113 ns/op 501 B/op 5 allocs/op +BenchmarkBeego_Param5 1000000 1269 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Param5 986820 1873 ns/op 864 B/op 6 allocs/op +BenchmarkChi_Param5 1000000 1156 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_Param5 3036331 400 ns/op 160 B/op 1 allocs/op +BenchmarkEcho_Param5 6447133 186 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Param5 10786068 110 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param5 844820 1944 ns/op 920 B/op 11 allocs/op +BenchmarkGoji_Param5 1474965 827 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Param5 442820 2516 ns/op 1392 B/op 11 allocs/op +BenchmarkGoJsonRest_Param5 507555 2711 ns/op 1097 B/op 16 allocs/op +BenchmarkGoRestful_Param5 216481 6093 ns/op 4288 B/op 14 allocs/op +BenchmarkGorillaMux_Param5 314402 3628 ns/op 1344 B/op 10 allocs/op +BenchmarkGowwwRouter_Param5 1624660 733 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Param5 13167324 92.0 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_Param5 1000000 1295 ns/op 576 B/op 6 allocs/op +BenchmarkKocha_Param5 1000000 1138 ns/op 440 B/op 10 allocs/op +BenchmarkLARS_Param5 11580613 105 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param5 473596 2755 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_Param5 230756 5111 ns/op 1232 B/op 11 allocs/op +BenchmarkPat_Param5 469190 3370 ns/op 888 B/op 29 allocs/op +BenchmarkPossum_Param5 1000000 1002 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Param5 1422129 844 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Param5 2263789 539 ns/op 240 B/op 1 allocs/op +BenchmarkTango_Param5 1000000 1256 ns/op 360 B/op 8 allocs/op +BenchmarkTigerTonic_Param5 175500 7492 ns/op 2279 B/op 39 allocs/op +BenchmarkTraffic_Param5 233631 5816 ns/op 2208 B/op 27 allocs/op +BenchmarkVulcan_Param5 1923416 629 ns/op 98 B/op 3 allocs/op +BenchmarkAce_Param20 4321266 281 ns/op 0 B/op 0 allocs/op +BenchmarkAero_Param20 31501641 35.2 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Param20 335204 3489 ns/op 1665 B/op 5 allocs/op +BenchmarkBeego_Param20 503674 2860 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Param20 298922 4741 ns/op 2031 B/op 6 allocs/op +BenchmarkChi_Param20 878181 1957 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_Param20 1000000 1360 ns/op 640 B/op 1 allocs/op +BenchmarkEcho_Param20 2104946 580 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Param20 4167204 290 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param20 173064 7514 ns/op 3796 B/op 15 allocs/op +BenchmarkGoji_Param20 458778 2651 ns/op 1247 B/op 2 allocs/op +BenchmarkGojiv2_Param20 364862 3178 ns/op 1632 B/op 11 allocs/op +BenchmarkGoJsonRest_Param20 125514 9760 ns/op 4485 B/op 20 allocs/op +BenchmarkGoRestful_Param20 101217 11964 ns/op 6715 B/op 18 allocs/op +BenchmarkGorillaMux_Param20 147654 8132 ns/op 3452 B/op 12 allocs/op +BenchmarkGowwwRouter_Param20 1000000 1225 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Param20 4920895 247 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_Param20 173202 6605 ns/op 3196 B/op 10 allocs/op +BenchmarkKocha_Param20 345988 3620 ns/op 1808 B/op 27 allocs/op +BenchmarkLARS_Param20 4592326 262 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param20 166492 7286 ns/op 2924 B/op 12 allocs/op +BenchmarkMartini_Param20 122162 10653 ns/op 3595 B/op 13 allocs/op +BenchmarkPat_Param20 78630 15239 ns/op 4424 B/op 93 allocs/op +BenchmarkPossum_Param20 1000000 1008 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Param20 294981 4587 ns/op 2284 B/op 7 allocs/op +BenchmarkRivet_Param20 691798 2090 ns/op 1024 B/op 1 allocs/op +BenchmarkTango_Param20 842440 2505 ns/op 856 B/op 8 allocs/op +BenchmarkTigerTonic_Param20 38614 31509 ns/op 9870 B/op 119 allocs/op +BenchmarkTraffic_Param20 57633 21107 ns/op 7853 B/op 47 allocs/op +BenchmarkVulcan_Param20 1000000 1178 ns/op 98 B/op 3 allocs/op +BenchmarkAce_ParamWrite 7330743 180 ns/op 8 B/op 1 allocs/op +BenchmarkAero_ParamWrite 13833598 86.7 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParamWrite 1363321 867 ns/op 456 B/op 5 allocs/op +BenchmarkBeego_ParamWrite 1000000 1104 ns/op 360 B/op 4 allocs/op +BenchmarkBone_ParamWrite 1000000 1475 ns/op 816 B/op 6 allocs/op +BenchmarkChi_ParamWrite 1320590 892 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_ParamWrite 7093605 172 ns/op 32 B/op 1 allocs/op +BenchmarkEcho_ParamWrite 8434424 161 ns/op 8 B/op 1 allocs/op +BenchmarkGin_ParamWrite 10377034 118 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParamWrite 1000000 1266 ns/op 656 B/op 9 allocs/op +BenchmarkGoji_ParamWrite 1874168 654 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_ParamWrite 459032 2352 ns/op 1360 B/op 13 allocs/op +BenchmarkGoJsonRest_ParamWrite 499434 2145 ns/op 1128 B/op 18 allocs/op +BenchmarkGoRestful_ParamWrite 241087 5470 ns/op 4200 B/op 15 allocs/op +BenchmarkGorillaMux_ParamWrite 425686 2522 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_ParamWrite 922172 1778 ns/op 976 B/op 8 allocs/op +BenchmarkHttpRouter_ParamWrite 15392049 77.7 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParamWrite 1973385 597 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_ParamWrite 4262500 281 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_ParamWrite 10764410 113 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParamWrite 486769 2726 ns/op 1176 B/op 14 allocs/op +BenchmarkMartini_ParamWrite 264804 4842 ns/op 1176 B/op 14 allocs/op +BenchmarkPat_ParamWrite 735116 2047 ns/op 960 B/op 15 allocs/op +BenchmarkPossum_ParamWrite 1000000 1004 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_ParamWrite 1592136 768 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_ParamWrite 3582051 339 ns/op 112 B/op 2 allocs/op +BenchmarkTango_ParamWrite 2237337 534 ns/op 136 B/op 4 allocs/op +BenchmarkTigerTonic_ParamWrite 439608 3136 ns/op 1216 B/op 21 allocs/op +BenchmarkTraffic_ParamWrite 306979 4328 ns/op 2280 B/op 25 allocs/op +BenchmarkVulcan_ParamWrite 2529973 472 ns/op 98 B/op 3 allocs/op ``` ## GitHub -``` -BenchmarkGin_GithubStatic 5866748 194 ns/op 0 B/op 0 allocs/op +```sh +BenchmarkGin_GithubStatic 15629472 76.7 ns/op 0 B/op 0 allocs/op -BenchmarkAce_GithubStatic 5815826 205 ns/op 0 B/op 0 allocs/op -BenchmarkAero_GithubStatic 10822906 106 ns/op 0 B/op 0 allocs/op -BenchmarkBear_GithubStatic 1678065 707 ns/op 120 B/op 3 allocs/op -BenchmarkBeego_GithubStatic 828814 1717 ns/op 352 B/op 3 allocs/op -BenchmarkBone_GithubStatic 67484 18858 ns/op 2880 B/op 60 allocs/op -BenchmarkCloudyKitRouter_GithubStatic 10219297 115 ns/op 0 B/op 0 allocs/op -BenchmarkChi_GithubStatic 1000000 1348 ns/op 432 B/op 3 allocs/op -BenchmarkDenco_GithubStatic 15220622 75.4 ns/op 0 B/op 0 allocs/op -BenchmarkEcho_GithubStatic 7255897 158 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GithubStatic 1000000 1198 ns/op 296 B/op 5 allocs/op -BenchmarkGoji_GithubStatic 3659361 320 ns/op 0 B/op 0 allocs/op -BenchmarkGojiv2_GithubStatic 402402 3384 ns/op 1312 B/op 10 allocs/op -BenchmarkGoRestful_GithubStatic 54592 22045 ns/op 4256 B/op 13 allocs/op -BenchmarkGoJsonRest_GithubStatic 801067 1673 ns/op 329 B/op 11 allocs/op -BenchmarkGorillaMux_GithubStatic 169690 8171 ns/op 976 B/op 9 allocs/op -BenchmarkGowwwRouter_GithubStatic 5372910 218 ns/op 0 B/op 0 allocs/op -BenchmarkHttpRouter_GithubStatic 10965576 103 ns/op 0 B/op 0 allocs/op -BenchmarkHttpTreeMux_GithubStatic 10505365 106 ns/op 0 B/op 0 allocs/op -BenchmarkKocha_GithubStatic 14153763 81.9 ns/op 0 B/op 0 allocs/op -BenchmarkLARS_GithubStatic 7874017 152 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_GithubStatic 696940 2678 ns/op 736 B/op 8 allocs/op -BenchmarkMartini_GithubStatic 102384 12276 ns/op 768 B/op 9 allocs/op -BenchmarkPat_GithubStatic 69907 17437 ns/op 3648 B/op 76 allocs/op -BenchmarkPossum_GithubStatic 1000000 1262 ns/op 416 B/op 3 allocs/op -BenchmarkR2router_GithubStatic 1981592 614 ns/op 144 B/op 4 allocs/op -BenchmarkRivet_GithubStatic 6103872 196 ns/op 0 B/op 0 allocs/op -BenchmarkTango_GithubStatic 629551 2023 ns/op 248 B/op 8 allocs/op -BenchmarkTigerTonic_GithubStatic 2801569 424 ns/op 48 B/op 1 allocs/op -BenchmarkTraffic_GithubStatic 63716 18009 ns/op 4664 B/op 90 allocs/op -BenchmarkVulcan_GithubStatic 885640 1177 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_GithubParam 2016942 582 ns/op 96 B/op 1 allocs/op -BenchmarkAero_GithubParam 4009522 296 ns/op 0 B/op 0 allocs/op -BenchmarkBear_GithubParam 1000000 1575 ns/op 496 B/op 5 allocs/op -BenchmarkBeego_GithubParam 796662 2038 ns/op 352 B/op 3 allocs/op -BenchmarkBone_GithubParam 114823 10325 ns/op 1888 B/op 19 allocs/op -BenchmarkChi_GithubParam 1000000 1783 ns/op 432 B/op 3 allocs/op -BenchmarkCloudyKitRouter_GithubParam 3910996 303 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_GithubParam 2298172 521 ns/op 128 B/op 1 allocs/op -BenchmarkEcho_GithubParam 3336364 357 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GithubParam 2729161 439 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GithubParam 825784 2338 ns/op 712 B/op 9 allocs/op -BenchmarkGoji_GithubParam 933397 1559 ns/op 336 B/op 2 allocs/op -BenchmarkGojiv2_GithubParam 253884 4335 ns/op 1408 B/op 13 allocs/op -BenchmarkGoJsonRest_GithubParam 575532 2967 ns/op 713 B/op 14 allocs/op -BenchmarkGoRestful_GithubParam 38160 30638 ns/op 4352 B/op 16 allocs/op -BenchmarkGorillaMux_GithubParam 94554 12035 ns/op 1296 B/op 10 allocs/op -BenchmarkGowwwRouter_GithubParam 1000000 1223 ns/op 432 B/op 3 allocs/op -BenchmarkHttpRouter_GithubParam 2562079 468 ns/op 96 B/op 1 allocs/op -BenchmarkHttpTreeMux_GithubParam 1000000 1386 ns/op 384 B/op 4 allocs/op -BenchmarkKocha_GithubParam 1573026 754 ns/op 128 B/op 5 allocs/op -BenchmarkLARS_GithubParam 4203394 282 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_GithubParam 365078 4137 ns/op 1072 B/op 10 allocs/op -BenchmarkMartini_GithubParam 71608 15811 ns/op 1152 B/op 11 allocs/op -BenchmarkPat_GithubParam 92768 13297 ns/op 2408 B/op 48 allocs/op -BenchmarkPossum_GithubParam 1000000 1704 ns/op 496 B/op 5 allocs/op -BenchmarkR2router_GithubParam 1000000 1120 ns/op 432 B/op 5 allocs/op -BenchmarkRivet_GithubParam 1642794 720 ns/op 96 B/op 1 allocs/op -BenchmarkTango_GithubParam 574195 2345 ns/op 344 B/op 8 allocs/op -BenchmarkTigerTonic_GithubParam 272430 5493 ns/op 1176 B/op 22 allocs/op -BenchmarkTraffic_GithubParam 81914 15078 ns/op 2816 B/op 40 allocs/op -BenchmarkVulcan_GithubParam 581272 1902 ns/op 98 B/op 3 allocs/op - - -BenchmarkAce_GithubAll 10000 103571 ns/op 13792 B/op 167 allocs/op -BenchmarkAero_GithubAll 21366 55615 ns/op 0 B/op 0 allocs/op -BenchmarkBear_GithubAll 5288 327648 ns/op 86448 B/op 943 allocs/op -BenchmarkBeego_GithubAll 3974 413453 ns/op 71456 B/op 609 allocs/op -BenchmarkBone_GithubAll 267 4450294 ns/op 720160 B/op 8620 allocs/op -BenchmarkChi_GithubAll 5067 358773 ns/op 87696 B/op 609 allocs/op -BenchmarkCloudyKitRouter_GithubAll 24210 49233 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_GithubAll 12508 95341 ns/op 20224 B/op 167 allocs/op -BenchmarkEcho_GithubAll 16353 73267 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GithubAll 15516 77716 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GithubAll 2908 466970 ns/op 131656 B/op 1686 allocs/op -BenchmarkGoji_GithubAll 1746 691392 ns/op 56112 B/op 334 allocs/op -BenchmarkGojiv2_GithubAll 954 1289604 ns/op 352720 B/op 4321 allocs/op -BenchmarkGoJsonRest_GithubAll 2013 599088 ns/op 134371 B/op 2737 allocs/op -BenchmarkGoRestful_GithubAll 223 5404307 ns/op 910144 B/op 2938 allocs/op -BenchmarkGorillaMux_GithubAll 202 5943565 ns/op 251650 B/op 1994 allocs/op -BenchmarkGowwwRouter_GithubAll 9009 227799 ns/op 72144 B/op 501 allocs/op -BenchmarkHttpRouter_GithubAll 14570 78718 ns/op 13792 B/op 167 allocs/op -BenchmarkHttpTreeMux_GithubAll 7226 242491 ns/op 65856 B/op 671 allocs/op -BenchmarkKocha_GithubAll 8282 159873 ns/op 23304 B/op 843 allocs/op -BenchmarkLARS_GithubAll 22711 52745 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_GithubAll 2067 563117 ns/op 149409 B/op 1624 allocs/op -BenchmarkMartini_GithubAll 218 5455290 ns/op 226552 B/op 2325 allocs/op -BenchmarkPat_GithubAll 174 6801582 ns/op 1483152 B/op 26963 allocs/op -BenchmarkPossum_GithubAll 8113 263665 ns/op 84448 B/op 609 allocs/op -BenchmarkR2router_GithubAll 7172 247198 ns/op 77328 B/op 979 allocs/op -BenchmarkRivet_GithubAll 10000 128086 ns/op 16272 B/op 167 allocs/op -BenchmarkTango_GithubAll 3316 472753 ns/op 63825 B/op 1618 allocs/op -BenchmarkTigerTonic_GithubAll 1176 1041991 ns/op 193856 B/op 4474 allocs/op -BenchmarkTraffic_GithubAll 226 5312082 ns/op 820742 B/op 14114 allocs/op -BenchmarkVulcan_GithubAll 3904 304440 ns/op 19894 B/op 609 allocs/op +BenchmarkAce_GithubStatic 15542612 75.9 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GithubStatic 24777151 48.5 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubStatic 2788894 435 ns/op 120 B/op 3 allocs/op +BenchmarkBeego_GithubStatic 1000000 1064 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GithubStatic 93507 12838 ns/op 2880 B/op 60 allocs/op +BenchmarkChi_GithubStatic 1387743 860 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GithubStatic 39384996 30.4 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_GithubStatic 12076382 99.1 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubStatic 1596495 756 ns/op 296 B/op 5 allocs/op +BenchmarkGoji_GithubStatic 6364876 189 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_GithubStatic 550202 2098 ns/op 1312 B/op 10 allocs/op +BenchmarkGoRestful_GithubStatic 102183 12552 ns/op 4256 B/op 13 allocs/op +BenchmarkGoJsonRest_GithubStatic 1000000 1029 ns/op 329 B/op 11 allocs/op +BenchmarkGorillaMux_GithubStatic 255552 5190 ns/op 976 B/op 9 allocs/op +BenchmarkGowwwRouter_GithubStatic 15531916 77.1 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_GithubStatic 27920724 43.1 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GithubStatic 21448953 55.8 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_GithubStatic 21405310 56.0 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_GithubStatic 13625156 89.0 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubStatic 1000000 1747 ns/op 736 B/op 8 allocs/op +BenchmarkMartini_GithubStatic 187186 7326 ns/op 768 B/op 9 allocs/op +BenchmarkPat_GithubStatic 109143 11563 ns/op 3648 B/op 76 allocs/op +BenchmarkPossum_GithubStatic 1575898 770 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_GithubStatic 3046231 404 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_GithubStatic 11484826 105 ns/op 0 B/op 0 allocs/op +BenchmarkTango_GithubStatic 1000000 1153 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_GithubStatic 4929780 249 ns/op 48 B/op 1 allocs/op +BenchmarkTraffic_GithubStatic 106351 11819 ns/op 4664 B/op 90 allocs/op +BenchmarkVulcan_GithubStatic 1613271 722 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GithubParam 8386032 143 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GithubParam 11816200 102 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubParam 1000000 1012 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_GithubParam 1000000 1157 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GithubParam 184653 6912 ns/op 1888 B/op 19 allocs/op +BenchmarkChi_GithubParam 1000000 1102 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GithubParam 3484798 352 ns/op 128 B/op 1 allocs/op +BenchmarkEcho_GithubParam 6337380 189 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GithubParam 9132032 131 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubParam 1000000 1446 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_GithubParam 1248640 977 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GithubParam 383233 2784 ns/op 1408 B/op 13 allocs/op +BenchmarkGoJsonRest_GithubParam 1000000 1991 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_GithubParam 76414 16015 ns/op 4352 B/op 16 allocs/op +BenchmarkGorillaMux_GithubParam 150026 7663 ns/op 1296 B/op 10 allocs/op +BenchmarkGowwwRouter_GithubParam 1592044 751 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_GithubParam 10420628 115 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GithubParam 1403755 835 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_GithubParam 2286170 533 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_GithubParam 9540374 129 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubParam 533154 2742 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_GithubParam 119397 9638 ns/op 1152 B/op 11 allocs/op +BenchmarkPat_GithubParam 150675 8858 ns/op 2408 B/op 48 allocs/op +BenchmarkPossum_GithubParam 1000000 1001 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_GithubParam 1602886 761 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GithubParam 2986579 409 ns/op 96 B/op 1 allocs/op +BenchmarkTango_GithubParam 1000000 1356 ns/op 344 B/op 8 allocs/op +BenchmarkTigerTonic_GithubParam 388899 3429 ns/op 1176 B/op 22 allocs/op +BenchmarkTraffic_GithubParam 123160 9734 ns/op 2816 B/op 40 allocs/op +BenchmarkVulcan_GithubParam 1000000 1138 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GithubAll 40543 29670 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GithubAll 57632 20648 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubAll 9234 216179 ns/op 86448 B/op 943 allocs/op +BenchmarkBeego_GithubAll 7407 243496 ns/op 71456 B/op 609 allocs/op +BenchmarkBone_GithubAll 420 2922835 ns/op 720160 B/op 8620 allocs/op +BenchmarkChi_GithubAll 7620 238331 ns/op 87696 B/op 609 allocs/op +BenchmarkDenco_GithubAll 18355 64494 ns/op 20224 B/op 167 allocs/op +BenchmarkEcho_GithubAll 31251 38479 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GithubAll 43550 27364 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubAll 4117 300062 ns/op 131656 B/op 1686 allocs/op +BenchmarkGoji_GithubAll 3274 416158 ns/op 56112 B/op 334 allocs/op +BenchmarkGojiv2_GithubAll 1402 870518 ns/op 352720 B/op 4321 allocs/op +BenchmarkGoJsonRest_GithubAll 2976 401507 ns/op 134371 B/op 2737 allocs/op +BenchmarkGoRestful_GithubAll 410 2913158 ns/op 910144 B/op 2938 allocs/op +BenchmarkGorillaMux_GithubAll 346 3384987 ns/op 251650 B/op 1994 allocs/op +BenchmarkGowwwRouter_GithubAll 10000 143025 ns/op 72144 B/op 501 allocs/op +BenchmarkHttpRouter_GithubAll 55938 21360 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GithubAll 10000 153944 ns/op 65856 B/op 671 allocs/op +BenchmarkKocha_GithubAll 10000 106315 ns/op 23304 B/op 843 allocs/op +BenchmarkLARS_GithubAll 47779 25084 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubAll 3266 371907 ns/op 149409 B/op 1624 allocs/op +BenchmarkMartini_GithubAll 331 3444706 ns/op 226551 B/op 2325 allocs/op +BenchmarkPat_GithubAll 273 4381818 ns/op 1483152 B/op 26963 allocs/op +BenchmarkPossum_GithubAll 10000 164367 ns/op 84448 B/op 609 allocs/op +BenchmarkR2router_GithubAll 10000 160220 ns/op 77328 B/op 979 allocs/op +BenchmarkRivet_GithubAll 14625 82453 ns/op 16272 B/op 167 allocs/op +BenchmarkTango_GithubAll 6255 279611 ns/op 63826 B/op 1618 allocs/op +BenchmarkTigerTonic_GithubAll 2008 687874 ns/op 193856 B/op 4474 allocs/op +BenchmarkTraffic_GithubAll 355 3478508 ns/op 820744 B/op 14114 allocs/op +BenchmarkVulcan_GithubAll 6885 193333 ns/op 19894 B/op 609 allocs/op ``` ## Google+ -``` -BenchmarkGin_GPlusStatic 9172405 124 ns/op 0 B/op 0 allocs/op +```sh +BenchmarkGin_GPlusStatic 19247326 62.2 ns/op 0 B/op 0 allocs/op -BenchmarkAce_GPlusStatic 7784710 152 ns/op 0 B/op 0 allocs/op -BenchmarkAero_GPlusStatic 12771894 89.2 ns/op 0 B/op 0 allocs/op -BenchmarkBear_GPlusStatic 2351325 512 ns/op 104 B/op 3 allocs/op -BenchmarkBeego_GPlusStatic 1000000 1643 ns/op 352 B/op 3 allocs/op -BenchmarkBone_GPlusStatic 4419217 263 ns/op 32 B/op 1 allocs/op -BenchmarkChi_GPlusStatic 1000000 1282 ns/op 432 B/op 3 allocs/op -BenchmarkCloudyKitRouter_GPlusStatic 17730754 61.9 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_GPlusStatic 29549895 38.3 ns/op 0 B/op 0 allocs/op -BenchmarkEcho_GPlusStatic 10521789 111 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GPlusStatic 1000000 1053 ns/op 280 B/op 5 allocs/op -BenchmarkGoji_GPlusStatic 5209968 228 ns/op 0 B/op 0 allocs/op -BenchmarkGojiv2_GPlusStatic 306363 3348 ns/op 1312 B/op 10 allocs/op -BenchmarkGoJsonRest_GPlusStatic 1000000 1424 ns/op 329 B/op 11 allocs/op -BenchmarkGoRestful_GPlusStatic 130754 8760 ns/op 3872 B/op 13 allocs/op -BenchmarkGorillaMux_GPlusStatic 496250 2860 ns/op 976 B/op 9 allocs/op -BenchmarkGowwwRouter_GPlusStatic 16401519 66.5 ns/op 0 B/op 0 allocs/op -BenchmarkHttpRouter_GPlusStatic 21323139 50.3 ns/op 0 B/op 0 allocs/op -BenchmarkHttpTreeMux_GPlusStatic 14877926 68.7 ns/op 0 B/op 0 allocs/op -BenchmarkKocha_GPlusStatic 18375128 57.6 ns/op 0 B/op 0 allocs/op -BenchmarkLARS_GPlusStatic 11153810 101 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_GPlusStatic 652598 2720 ns/op 736 B/op 8 allocs/op -BenchmarkMartini_GPlusStatic 218824 6532 ns/op 768 B/op 9 allocs/op -BenchmarkPat_GPlusStatic 2825560 428 ns/op 96 B/op 2 allocs/op -BenchmarkPossum_GPlusStatic 1000000 1236 ns/op 416 B/op 3 allocs/op -BenchmarkR2router_GPlusStatic 2222193 541 ns/op 144 B/op 4 allocs/op -BenchmarkRivet_GPlusStatic 9802023 114 ns/op 0 B/op 0 allocs/op -BenchmarkTango_GPlusStatic 980658 1465 ns/op 200 B/op 8 allocs/op -BenchmarkTigerTonic_GPlusStatic 4882701 239 ns/op 32 B/op 1 allocs/op -BenchmarkTraffic_GPlusStatic 508060 3465 ns/op 1112 B/op 16 allocs/op -BenchmarkVulcan_GPlusStatic 1608979 725 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_GPlusParam 2962957 414 ns/op 64 B/op 1 allocs/op -BenchmarkAero_GPlusParam 5667668 202 ns/op 0 B/op 0 allocs/op -BenchmarkBear_GPlusParam 1000000 1271 ns/op 480 B/op 5 allocs/op -BenchmarkBeego_GPlusParam 869858 1874 ns/op 352 B/op 3 allocs/op -BenchmarkBone_GPlusParam 869476 2395 ns/op 816 B/op 6 allocs/op -BenchmarkChi_GPlusParam 1000000 1469 ns/op 432 B/op 3 allocs/op -BenchmarkCloudyKitRouter_GPlusParam 11149783 108 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_GPlusParam 4007298 301 ns/op 64 B/op 1 allocs/op -BenchmarkEcho_GPlusParam 6448201 174 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GPlusParam 5470827 218 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GPlusParam 1000000 1939 ns/op 648 B/op 8 allocs/op -BenchmarkGoji_GPlusParam 1207621 997 ns/op 336 B/op 2 allocs/op -BenchmarkGojiv2_GPlusParam 271326 4013 ns/op 1328 B/op 11 allocs/op -BenchmarkGoJsonRest_GPlusParam 781062 2303 ns/op 649 B/op 13 allocs/op -BenchmarkGoRestful_GPlusParam 121267 9871 ns/op 4192 B/op 14 allocs/op -BenchmarkGorillaMux_GPlusParam 228406 5156 ns/op 1280 B/op 10 allocs/op -BenchmarkGowwwRouter_GPlusParam 1000000 1074 ns/op 432 B/op 3 allocs/op -BenchmarkHttpRouter_GPlusParam 4399740 276 ns/op 64 B/op 1 allocs/op -BenchmarkHttpTreeMux_GPlusParam 1309540 898 ns/op 352 B/op 3 allocs/op -BenchmarkKocha_GPlusParam 2930965 403 ns/op 56 B/op 3 allocs/op -BenchmarkLARS_GPlusParam 7588237 151 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_GPlusParam 434997 4195 ns/op 1072 B/op 10 allocs/op -BenchmarkMartini_GPlusParam 148207 8144 ns/op 1072 B/op 10 allocs/op -BenchmarkPat_GPlusParam 566829 2533 ns/op 576 B/op 11 allocs/op -BenchmarkPossum_GPlusParam 1000000 1723 ns/op 496 B/op 5 allocs/op -BenchmarkR2router_GPlusParam 1000000 1100 ns/op 432 B/op 5 allocs/op -BenchmarkRivet_GPlusParam 3309052 331 ns/op 48 B/op 1 allocs/op -BenchmarkTango_GPlusParam 693728 1825 ns/op 264 B/op 8 allocs/op -BenchmarkTigerTonic_GPlusParam 417693 3800 ns/op 856 B/op 16 allocs/op -BenchmarkTraffic_GPlusParam 179424 6641 ns/op 1872 B/op 21 allocs/op -BenchmarkVulcan_GPlusParam 1000000 1063 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_GPlus2Params 2720149 460 ns/op 64 B/op 1 allocs/op -BenchmarkAero_GPlus2Params 3525165 343 ns/op 0 B/op 0 allocs/op -BenchmarkBear_GPlus2Params 1000000 1502 ns/op 496 B/op 5 allocs/op -BenchmarkBeego_GPlus2Params 730123 2102 ns/op 352 B/op 3 allocs/op -BenchmarkBone_GPlus2Params 253177 5583 ns/op 1168 B/op 10 allocs/op -BenchmarkChi_GPlus2Params 1000000 1531 ns/op 432 B/op 3 allocs/op -BenchmarkCloudyKitRouter_GPlus2Params 6943176 168 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_GPlus2Params 2912601 413 ns/op 64 B/op 1 allocs/op -BenchmarkEcho_GPlus2Params 4149189 278 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GPlus2Params 3271269 356 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GPlus2Params 915531 2321 ns/op 712 B/op 9 allocs/op -BenchmarkGoji_GPlus2Params 1000000 1413 ns/op 336 B/op 2 allocs/op -BenchmarkGojiv2_GPlus2Params 256640 4521 ns/op 1408 B/op 14 allocs/op -BenchmarkGoJsonRest_GPlus2Params 499140 3076 ns/op 713 B/op 14 allocs/op -BenchmarkGoRestful_GPlus2Params 105928 10148 ns/op 4384 B/op 16 allocs/op -BenchmarkGorillaMux_GPlus2Params 110953 9682 ns/op 1296 B/op 10 allocs/op -BenchmarkGowwwRouter_GPlus2Params 1000000 1112 ns/op 432 B/op 3 allocs/op -BenchmarkHttpRouter_GPlus2Params 3491893 321 ns/op 64 B/op 1 allocs/op -BenchmarkHttpTreeMux_GPlus2Params 1000000 1341 ns/op 384 B/op 4 allocs/op -BenchmarkKocha_GPlus2Params 1445288 790 ns/op 128 B/op 5 allocs/op -BenchmarkLARS_GPlus2Params 6644953 185 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_GPlus2Params 424291 4321 ns/op 1072 B/op 10 allocs/op -BenchmarkMartini_GPlus2Params 70866 16407 ns/op 1200 B/op 13 allocs/op -BenchmarkPat_GPlus2Params 121308 10221 ns/op 2168 B/op 33 allocs/op -BenchmarkPossum_GPlus2Params 1000000 1847 ns/op 496 B/op 5 allocs/op -BenchmarkR2router_GPlus2Params 1000000 1267 ns/op 432 B/op 5 allocs/op -BenchmarkRivet_GPlus2Params 2017526 590 ns/op 96 B/op 1 allocs/op -BenchmarkTango_GPlus2Params 846003 2143 ns/op 344 B/op 8 allocs/op -BenchmarkTigerTonic_GPlus2Params 303597 5736 ns/op 1200 B/op 22 allocs/op -BenchmarkTraffic_GPlus2Params 95032 12817 ns/op 2248 B/op 28 allocs/op -BenchmarkVulcan_GPlus2Params 692610 1575 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_GPlusAll 271720 4948 ns/op 640 B/op 11 allocs/op -BenchmarkAero_GPlusAll 367956 2926 ns/op 0 B/op 0 allocs/op -BenchmarkBear_GPlusAll 68161 17883 ns/op 5488 B/op 61 allocs/op -BenchmarkBeego_GPlusAll 46634 25369 ns/op 4576 B/op 39 allocs/op -BenchmarkBone_GPlusAll 24628 49198 ns/op 11744 B/op 109 allocs/op -BenchmarkChi_GPlusAll 60778 19356 ns/op 5616 B/op 39 allocs/op -BenchmarkCloudyKitRouter_GPlusAll 706952 1693 ns/op 0 B/op 0 allocs/op -BenchmarkDenco_GPlusAll 327422 4222 ns/op 672 B/op 11 allocs/op -BenchmarkEcho_GPlusAll 331987 3176 ns/op 0 B/op 0 allocs/op -BenchmarkGin_GPlusAll 289526 3559 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_GPlusAll 45805 26768 ns/op 8040 B/op 103 allocs/op -BenchmarkGoji_GPlusAll 74786 14428 ns/op 3696 B/op 22 allocs/op -BenchmarkGojiv2_GPlusAll 23822 50355 ns/op 17616 B/op 154 allocs/op -BenchmarkGoJsonRest_GPlusAll 35280 32989 ns/op 8117 B/op 170 allocs/op -BenchmarkGoRestful_GPlusAll 10000 129418 ns/op 55520 B/op 192 allocs/op -BenchmarkGorillaMux_GPlusAll 15968 76492 ns/op 16112 B/op 128 allocs/op -BenchmarkGowwwRouter_GPlusAll 100096 12644 ns/op 4752 B/op 33 allocs/op -BenchmarkHttpRouter_GPlusAll 474584 3704 ns/op 640 B/op 11 allocs/op -BenchmarkHttpTreeMux_GPlusAll 98506 12480 ns/op 4032 B/op 38 allocs/op -BenchmarkKocha_GPlusAll 213709 7358 ns/op 976 B/op 43 allocs/op -BenchmarkLARS_GPlusAll 466608 2363 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_GPlusAll 34136 35790 ns/op 9568 B/op 104 allocs/op -BenchmarkMartini_GPlusAll 8911 124543 ns/op 14016 B/op 145 allocs/op -BenchmarkPat_GPlusAll 17391 69198 ns/op 15264 B/op 271 allocs/op -BenchmarkPossum_GPlusAll 66774 17004 ns/op 5408 B/op 39 allocs/op -BenchmarkR2router_GPlusAll 79681 13996 ns/op 5040 B/op 63 allocs/op -BenchmarkRivet_GPlusAll 258788 5344 ns/op 768 B/op 11 allocs/op -BenchmarkTango_GPlusAll 46930 25591 ns/op 3656 B/op 104 allocs/op -BenchmarkTigerTonic_GPlusAll 20768 58038 ns/op 11600 B/op 242 allocs/op -BenchmarkTraffic_GPlusAll 10000 108031 ns/op 26248 B/op 341 allocs/op -BenchmarkVulcan_GPlusAll 71826 15724 ns/op 1274 B/op 39 allocs/op +BenchmarkAce_GPlusStatic 20235060 59.2 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GPlusStatic 31978935 37.6 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusStatic 3516523 341 ns/op 104 B/op 3 allocs/op +BenchmarkBeego_GPlusStatic 1212036 991 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GPlusStatic 6736242 183 ns/op 32 B/op 1 allocs/op +BenchmarkChi_GPlusStatic 1490640 814 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GPlusStatic 55006856 21.8 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_GPlusStatic 17688258 67.9 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusStatic 1829181 666 ns/op 280 B/op 5 allocs/op +BenchmarkGoji_GPlusStatic 9147451 130 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_GPlusStatic 594015 2063 ns/op 1312 B/op 10 allocs/op +BenchmarkGoJsonRest_GPlusStatic 1264906 950 ns/op 329 B/op 11 allocs/op +BenchmarkGoRestful_GPlusStatic 231558 5341 ns/op 3872 B/op 13 allocs/op +BenchmarkGorillaMux_GPlusStatic 908418 1809 ns/op 976 B/op 9 allocs/op +BenchmarkGowwwRouter_GPlusStatic 40684604 29.5 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_GPlusStatic 46742804 25.7 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlusStatic 32567161 36.9 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_GPlusStatic 33800060 35.3 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_GPlusStatic 20431858 60.0 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusStatic 1000000 1745 ns/op 736 B/op 8 allocs/op +BenchmarkMartini_GPlusStatic 442248 3619 ns/op 768 B/op 9 allocs/op +BenchmarkPat_GPlusStatic 4328004 292 ns/op 96 B/op 2 allocs/op +BenchmarkPossum_GPlusStatic 1570753 763 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_GPlusStatic 3339474 355 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_GPlusStatic 18570961 64.7 ns/op 0 B/op 0 allocs/op +BenchmarkTango_GPlusStatic 1388702 860 ns/op 200 B/op 8 allocs/op +BenchmarkTigerTonic_GPlusStatic 7803543 159 ns/op 32 B/op 1 allocs/op +BenchmarkTraffic_GPlusStatic 878605 2171 ns/op 1112 B/op 16 allocs/op +BenchmarkVulcan_GPlusStatic 2742446 437 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GPlusParam 11626975 105 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GPlusParam 16914322 71.6 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusParam 1405173 832 ns/op 480 B/op 5 allocs/op +BenchmarkBeego_GPlusParam 1000000 1075 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GPlusParam 1000000 1557 ns/op 816 B/op 6 allocs/op +BenchmarkChi_GPlusParam 1347926 894 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GPlusParam 5513000 212 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_GPlusParam 11884383 101 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlusParam 12898952 93.1 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusParam 1000000 1194 ns/op 648 B/op 8 allocs/op +BenchmarkGoji_GPlusParam 1857229 645 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GPlusParam 520939 2322 ns/op 1328 B/op 11 allocs/op +BenchmarkGoJsonRest_GPlusParam 1000000 1536 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_GPlusParam 205449 5800 ns/op 4192 B/op 14 allocs/op +BenchmarkGorillaMux_GPlusParam 395310 3188 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_GPlusParam 1851798 667 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_GPlusParam 18420789 65.2 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlusParam 1878463 629 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_GPlusParam 4495610 273 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_GPlusParam 14615976 83.2 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusParam 584145 2549 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_GPlusParam 250501 4583 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_GPlusParam 1000000 1645 ns/op 576 B/op 11 allocs/op +BenchmarkPossum_GPlusParam 1000000 1008 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_GPlusParam 1708191 688 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GPlusParam 5795014 211 ns/op 48 B/op 1 allocs/op +BenchmarkTango_GPlusParam 1000000 1091 ns/op 264 B/op 8 allocs/op +BenchmarkTigerTonic_GPlusParam 760221 2489 ns/op 856 B/op 16 allocs/op +BenchmarkTraffic_GPlusParam 309774 4039 ns/op 1872 B/op 21 allocs/op +BenchmarkVulcan_GPlusParam 1935730 623 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GPlus2Params 9158314 134 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GPlus2Params 11300517 107 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlus2Params 1239238 961 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_GPlus2Params 1000000 1202 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GPlus2Params 335576 3725 ns/op 1168 B/op 10 allocs/op +BenchmarkChi_GPlus2Params 1000000 1014 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GPlus2Params 4394598 280 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_GPlus2Params 7851861 154 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlus2Params 9958588 120 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlus2Params 1000000 1433 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_GPlus2Params 1325134 909 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GPlus2Params 405955 2870 ns/op 1408 B/op 14 allocs/op +BenchmarkGoJsonRest_GPlus2Params 977038 1987 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_GPlus2Params 205018 6142 ns/op 4384 B/op 16 allocs/op +BenchmarkGorillaMux_GPlus2Params 205641 6015 ns/op 1296 B/op 10 allocs/op +BenchmarkGowwwRouter_GPlus2Params 1748542 684 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_GPlus2Params 14047102 87.7 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlus2Params 1418673 828 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_GPlus2Params 2334562 520 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_GPlus2Params 11954094 101 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlus2Params 491552 2890 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_GPlus2Params 120532 9545 ns/op 1200 B/op 13 allocs/op +BenchmarkPat_GPlus2Params 194739 6766 ns/op 2168 B/op 33 allocs/op +BenchmarkPossum_GPlus2Params 1201224 1009 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_GPlus2Params 1575535 756 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GPlus2Params 3698930 325 ns/op 96 B/op 1 allocs/op +BenchmarkTango_GPlus2Params 1000000 1212 ns/op 344 B/op 8 allocs/op +BenchmarkTigerTonic_GPlus2Params 349350 3660 ns/op 1200 B/op 22 allocs/op +BenchmarkTraffic_GPlus2Params 169714 7862 ns/op 2248 B/op 28 allocs/op +BenchmarkVulcan_GPlus2Params 1222288 974 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GPlusAll 845606 1398 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GPlusAll 1000000 1009 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusAll 103830 11386 ns/op 5488 B/op 61 allocs/op +BenchmarkBeego_GPlusAll 82653 14784 ns/op 4576 B/op 39 allocs/op +BenchmarkBone_GPlusAll 36601 33123 ns/op 11744 B/op 109 allocs/op +BenchmarkChi_GPlusAll 95264 12831 ns/op 5616 B/op 39 allocs/op +BenchmarkDenco_GPlusAll 567681 2950 ns/op 672 B/op 11 allocs/op +BenchmarkEcho_GPlusAll 720366 1665 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlusAll 1000000 1185 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusAll 71575 16365 ns/op 8040 B/op 103 allocs/op +BenchmarkGoji_GPlusAll 136352 9191 ns/op 3696 B/op 22 allocs/op +BenchmarkGojiv2_GPlusAll 38006 31802 ns/op 17616 B/op 154 allocs/op +BenchmarkGoJsonRest_GPlusAll 57238 21561 ns/op 8117 B/op 170 allocs/op +BenchmarkGoRestful_GPlusAll 15147 79276 ns/op 55520 B/op 192 allocs/op +BenchmarkGorillaMux_GPlusAll 24446 48410 ns/op 16112 B/op 128 allocs/op +BenchmarkGowwwRouter_GPlusAll 150112 7770 ns/op 4752 B/op 33 allocs/op +BenchmarkHttpRouter_GPlusAll 1367820 878 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlusAll 166628 8004 ns/op 4032 B/op 38 allocs/op +BenchmarkKocha_GPlusAll 265694 4570 ns/op 976 B/op 43 allocs/op +BenchmarkLARS_GPlusAll 1000000 1068 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusAll 54564 23305 ns/op 9568 B/op 104 allocs/op +BenchmarkMartini_GPlusAll 16274 73845 ns/op 14016 B/op 145 allocs/op +BenchmarkPat_GPlusAll 27181 44478 ns/op 15264 B/op 271 allocs/op +BenchmarkPossum_GPlusAll 122587 10277 ns/op 5408 B/op 39 allocs/op +BenchmarkR2router_GPlusAll 130137 9297 ns/op 5040 B/op 63 allocs/op +BenchmarkRivet_GPlusAll 532438 3323 ns/op 768 B/op 11 allocs/op +BenchmarkTango_GPlusAll 86054 14531 ns/op 3656 B/op 104 allocs/op +BenchmarkTigerTonic_GPlusAll 33936 35356 ns/op 11600 B/op 242 allocs/op +BenchmarkTraffic_GPlusAll 17833 68181 ns/op 26248 B/op 341 allocs/op +BenchmarkVulcan_GPlusAll 120109 9861 ns/op 1274 B/op 39 allocs/op ``` ## Parse.com -``` -BenchmarkGin_ParseStatic 8683893 140 ns/op 0 B/op 0 allocs/op - -BenchmarkAce_ParseStatic 7255582 160 ns/op 0 B/op 0 allocs/op -BenchmarkAero_ParseStatic 11960128 95.0 ns/op 0 B/op 0 allocs/op -BenchmarkBear_ParseStatic 1791033 659 ns/op 120 B/op 3 allocs/op -BenchmarkBeego_ParseStatic 937918 1688 ns/op 352 B/op 3 allocs/op -BenchmarkBone_ParseStatic 1261682 949 ns/op 144 B/op 3 allocs/op -BenchmarkChi_ParseStatic 1000000 1303 ns/op 432 B/op 3 allocs/op -BenchmarkDenco_ParseStatic 23731242 49.8 ns/op 0 B/op 0 allocs/op -BenchmarkEcho_ParseStatic 10585060 116 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_ParseStatic 1000000 1156 ns/op 296 B/op 5 allocs/op -BenchmarkGoji_ParseStatic 3927530 300 ns/op 0 B/op 0 allocs/op -BenchmarkGojiv2_ParseStatic 474836 3281 ns/op 1312 B/op 10 allocs/op -BenchmarkGoJsonRest_ParseStatic 1000000 1445 ns/op 329 B/op 11 allocs/op -BenchmarkGoRestful_ParseStatic 101262 11612 ns/op 4256 B/op 13 allocs/op -BenchmarkGorillaMux_ParseStatic 562705 3530 ns/op 976 B/op 9 allocs/op -BenchmarkGowwwRouter_ParseStatic 16479007 69.5 ns/op 0 B/op 0 allocs/op -BenchmarkHttpRouter_ParseStatic 23205590 51.5 ns/op 0 B/op 0 allocs/op -BenchmarkHttpTreeMux_ParseStatic 10763127 106 ns/op 0 B/op 0 allocs/op -BenchmarkKocha_ParseStatic 17850259 60.9 ns/op 0 B/op 0 allocs/op -BenchmarkLARS_ParseStatic 10727432 108 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_ParseStatic 685586 2665 ns/op 736 B/op 8 allocs/op -BenchmarkMartini_ParseStatic 200642 7158 ns/op 768 B/op 9 allocs/op -BenchmarkPat_ParseStatic 1000000 1139 ns/op 240 B/op 5 allocs/op -BenchmarkPossum_ParseStatic 1000000 1241 ns/op 416 B/op 3 allocs/op -BenchmarkR2router_ParseStatic 2035426 597 ns/op 144 B/op 4 allocs/op -BenchmarkRivet_ParseStatic 9707011 127 ns/op 0 B/op 0 allocs/op -BenchmarkTango_ParseStatic 910617 1693 ns/op 248 B/op 8 allocs/op -BenchmarkTigerTonic_ParseStatic 3168885 385 ns/op 48 B/op 1 allocs/op -BenchmarkTraffic_ParseStatic 493339 4264 ns/op 1256 B/op 19 allocs/op -BenchmarkVulcan_ParseStatic 1394142 848 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_ParseParam 3106903 387 ns/op 64 B/op 1 allocs/op -BenchmarkAero_ParseParam 8045266 141 ns/op 0 B/op 0 allocs/op -BenchmarkBear_ParseParam 1000000 1434 ns/op 467 B/op 5 allocs/op -BenchmarkBeego_ParseParam 951460 1937 ns/op 352 B/op 3 allocs/op -BenchmarkBone_ParseParam 855555 2776 ns/op 896 B/op 7 allocs/op -BenchmarkChi_ParseParam 1000000 1457 ns/op 432 B/op 3 allocs/op -BenchmarkDenco_ParseParam 4084116 301 ns/op 64 B/op 1 allocs/op -BenchmarkEcho_ParseParam 8440170 142 ns/op 0 B/op 0 allocs/op -BenchmarkGin_ParseParam 7716948 157 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_ParseParam 886284 2045 ns/op 664 B/op 8 allocs/op -BenchmarkGoji_ParseParam 1000000 1167 ns/op 336 B/op 2 allocs/op -BenchmarkGojiv2_ParseParam 269731 3945 ns/op 1360 B/op 12 allocs/op -BenchmarkGoJsonRest_ParseParam 719587 2277 ns/op 649 B/op 13 allocs/op -BenchmarkGoRestful_ParseParam 96408 11925 ns/op 4576 B/op 14 allocs/op -BenchmarkGorillaMux_ParseParam 289303 4154 ns/op 1280 B/op 10 allocs/op -BenchmarkGowwwRouter_ParseParam 1000000 1070 ns/op 432 B/op 3 allocs/op -BenchmarkHttpRouter_ParseParam 4917758 232 ns/op 64 B/op 1 allocs/op -BenchmarkHttpTreeMux_ParseParam 1445443 828 ns/op 352 B/op 3 allocs/op -BenchmarkKocha_ParseParam 3116233 382 ns/op 56 B/op 3 allocs/op -BenchmarkLARS_ParseParam 10584750 113 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_ParseParam 413617 3872 ns/op 1072 B/op 10 allocs/op -BenchmarkMartini_ParseParam 166545 7605 ns/op 1072 B/op 10 allocs/op -BenchmarkPat_ParseParam 491829 3394 ns/op 992 B/op 15 allocs/op -BenchmarkPossum_ParseParam 1000000 1692 ns/op 496 B/op 5 allocs/op -BenchmarkR2router_ParseParam 1000000 1059 ns/op 432 B/op 5 allocs/op -BenchmarkRivet_ParseParam 3572359 311 ns/op 48 B/op 1 allocs/op -BenchmarkTango_ParseParam 787552 1889 ns/op 280 B/op 8 allocs/op -BenchmarkTigerTonic_ParseParam 487208 3706 ns/op 784 B/op 15 allocs/op -BenchmarkTraffic_ParseParam 186190 5812 ns/op 1896 B/op 21 allocs/op -BenchmarkVulcan_ParseParam 1275432 892 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_Parse2Params 2959621 412 ns/op 64 B/op 1 allocs/op -BenchmarkAero_Parse2Params 6208641 192 ns/op 0 B/op 0 allocs/op -BenchmarkBear_Parse2Params 1000000 1512 ns/op 496 B/op 5 allocs/op -BenchmarkBeego_Parse2Params 761940 1973 ns/op 352 B/op 3 allocs/op -BenchmarkBone_Parse2Params 715987 2582 ns/op 848 B/op 6 allocs/op -BenchmarkChi_Parse2Params 1000000 1495 ns/op 432 B/op 3 allocs/op -BenchmarkDenco_Parse2Params 3585452 341 ns/op 64 B/op 1 allocs/op -BenchmarkEcho_Parse2Params 5193693 204 ns/op 0 B/op 0 allocs/op -BenchmarkGin_Parse2Params 5338316 236 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_Parse2Params 939637 2299 ns/op 712 B/op 9 allocs/op -BenchmarkGoji_Parse2Params 1000000 1094 ns/op 336 B/op 2 allocs/op -BenchmarkGojiv2_Parse2Params 339514 3733 ns/op 1344 B/op 11 allocs/op -BenchmarkGoJsonRest_Parse2Params 512572 2733 ns/op 713 B/op 14 allocs/op -BenchmarkGoRestful_Parse2Params 95913 12973 ns/op 4928 B/op 14 allocs/op -BenchmarkGorillaMux_Parse2Params 261208 4758 ns/op 1296 B/op 10 allocs/op -BenchmarkGowwwRouter_Parse2Params 1000000 1084 ns/op 432 B/op 3 allocs/op -BenchmarkHttpRouter_Parse2Params 4399953 277 ns/op 64 B/op 1 allocs/op -BenchmarkHttpTreeMux_Parse2Params 1000000 1198 ns/op 384 B/op 4 allocs/op -BenchmarkKocha_Parse2Params 1669431 683 ns/op 128 B/op 5 allocs/op -BenchmarkLARS_Parse2Params 8535754 142 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_Parse2Params 424590 3959 ns/op 1072 B/op 10 allocs/op -BenchmarkMartini_Parse2Params 162448 8141 ns/op 1152 B/op 11 allocs/op -BenchmarkPat_Parse2Params 431336 3484 ns/op 752 B/op 16 allocs/op -BenchmarkPossum_Parse2Params 1000000 1721 ns/op 496 B/op 5 allocs/op -BenchmarkR2router_Parse2Params 1000000 1136 ns/op 432 B/op 5 allocs/op -BenchmarkRivet_Parse2Params 2630935 442 ns/op 96 B/op 1 allocs/op -BenchmarkTango_Parse2Params 759218 1876 ns/op 312 B/op 8 allocs/op -BenchmarkTigerTonic_Parse2Params 290810 5558 ns/op 1168 B/op 22 allocs/op -BenchmarkTraffic_Parse2Params 181099 6917 ns/op 1944 B/op 22 allocs/op -BenchmarkVulcan_Parse2Params 1000000 1080 ns/op 98 B/op 3 allocs/op - -BenchmarkAce_ParseAll 162906 7888 ns/op 640 B/op 16 allocs/op -BenchmarkAero_ParseAll 219260 4833 ns/op 0 B/op 0 allocs/op -BenchmarkBear_ParseAll 37566 32863 ns/op 8928 B/op 110 allocs/op -BenchmarkBeego_ParseAll 25400 46518 ns/op 9152 B/op 78 allocs/op -BenchmarkBone_ParseAll 19568 61814 ns/op 16208 B/op 147 allocs/op -BenchmarkChi_ParseAll 30562 38281 ns/op 11232 B/op 78 allocs/op -BenchmarkDenco_ParseAll 232554 6371 ns/op 928 B/op 16 allocs/op -BenchmarkEcho_ParseAll 224400 5090 ns/op 0 B/op 0 allocs/op -BenchmarkGin_ParseAll 189829 6134 ns/op 0 B/op 0 allocs/op -BenchmarkGocraftWeb_ParseAll 25446 47000 ns/op 13728 B/op 181 allocs/op -BenchmarkGoji_ParseAll 50503 22949 ns/op 5376 B/op 32 allocs/op -BenchmarkGojiv2_ParseAll 12806 93106 ns/op 34448 B/op 277 allocs/op -BenchmarkGoJsonRest_ParseAll 20764 57021 ns/op 13866 B/op 321 allocs/op -BenchmarkGoRestful_ParseAll 4234 317238 ns/op 117600 B/op 354 allocs/op -BenchmarkGorillaMux_ParseAll 10000 146942 ns/op 30288 B/op 250 allocs/op -BenchmarkGowwwRouter_ParseAll 62548 19363 ns/op 6912 B/op 48 allocs/op -BenchmarkHttpRouter_ParseAll 286662 5091 ns/op 640 B/op 16 allocs/op -BenchmarkHttpTreeMux_ParseAll 66952 18262 ns/op 5728 B/op 51 allocs/op -BenchmarkKocha_ParseAll 109771 9811 ns/op 1112 B/op 54 allocs/op -BenchmarkLARS_ParseAll 272516 3976 ns/op 0 B/op 0 allocs/op -BenchmarkMacaron_ParseAll 17094 71634 ns/op 19136 B/op 208 allocs/op -BenchmarkMartini_ParseAll 6799 208122 ns/op 25072 B/op 253 allocs/op -BenchmarkPat_ParseAll 15993 74594 ns/op 15216 B/op 308 allocs/op -BenchmarkPossum_ParseAll 34897 33398 ns/op 10816 B/op 78 allocs/op -BenchmarkR2router_ParseAll 46909 25410 ns/op 8352 B/op 120 allocs/op -BenchmarkRivet_ParseAll 185193 7725 ns/op 912 B/op 16 allocs/op -BenchmarkTango_ParseAll 24481 47963 ns/op 7168 B/op 208 allocs/op -BenchmarkTigerTonic_ParseAll 15236 79623 ns/op 16048 B/op 332 allocs/op -BenchmarkTraffic_ParseAll 8955 169411 ns/op 45520 B/op 605 allocs/op -BenchmarkVulcan_ParseAll 40406 28971 ns/op 2548 B/op 78 allocs/op +```sh +BenchmarkGin_ParseStatic 18877833 63.5 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_ParseStatic 19663731 60.8 ns/op 0 B/op 0 allocs/op +BenchmarkAero_ParseStatic 28967341 41.5 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseStatic 3006984 402 ns/op 120 B/op 3 allocs/op +BenchmarkBeego_ParseStatic 1000000 1031 ns/op 352 B/op 3 allocs/op +BenchmarkBone_ParseStatic 1782482 675 ns/op 144 B/op 3 allocs/op +BenchmarkChi_ParseStatic 1453261 819 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_ParseStatic 45023595 26.5 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_ParseStatic 17330470 69.3 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseStatic 1644006 731 ns/op 296 B/op 5 allocs/op +BenchmarkGoji_ParseStatic 7026930 170 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_ParseStatic 517618 2037 ns/op 1312 B/op 10 allocs/op +BenchmarkGoJsonRest_ParseStatic 1227080 975 ns/op 329 B/op 11 allocs/op +BenchmarkGoRestful_ParseStatic 192458 6659 ns/op 4256 B/op 13 allocs/op +BenchmarkGorillaMux_ParseStatic 744062 2109 ns/op 976 B/op 9 allocs/op +BenchmarkGowwwRouter_ParseStatic 37781062 31.8 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_ParseStatic 45311223 26.5 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParseStatic 21383475 56.1 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_ParseStatic 29953290 40.1 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_ParseStatic 20036196 62.7 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseStatic 1000000 1740 ns/op 736 B/op 8 allocs/op +BenchmarkMartini_ParseStatic 404156 3801 ns/op 768 B/op 9 allocs/op +BenchmarkPat_ParseStatic 1547180 772 ns/op 240 B/op 5 allocs/op +BenchmarkPossum_ParseStatic 1608991 757 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_ParseStatic 3177936 385 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_ParseStatic 17783205 67.4 ns/op 0 B/op 0 allocs/op +BenchmarkTango_ParseStatic 1210777 990 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_ParseStatic 5316440 231 ns/op 48 B/op 1 allocs/op +BenchmarkTraffic_ParseStatic 496050 2539 ns/op 1256 B/op 19 allocs/op +BenchmarkVulcan_ParseStatic 2462798 488 ns/op 98 B/op 3 allocs/op +BenchmarkAce_ParseParam 13393669 89.6 ns/op 0 B/op 0 allocs/op +BenchmarkAero_ParseParam 19836619 60.4 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseParam 1405954 864 ns/op 467 B/op 5 allocs/op +BenchmarkBeego_ParseParam 1000000 1065 ns/op 352 B/op 3 allocs/op +BenchmarkBone_ParseParam 1000000 1698 ns/op 896 B/op 7 allocs/op +BenchmarkChi_ParseParam 1356037 873 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_ParseParam 6241392 204 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_ParseParam 14088100 85.1 ns/op 0 B/op 0 allocs/op +BenchmarkGin_ParseParam 17426064 68.9 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseParam 1000000 1254 ns/op 664 B/op 8 allocs/op +BenchmarkGoji_ParseParam 1682574 713 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_ParseParam 502224 2333 ns/op 1360 B/op 12 allocs/op +BenchmarkGoJsonRest_ParseParam 1000000 1401 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_ParseParam 182623 7097 ns/op 4576 B/op 14 allocs/op +BenchmarkGorillaMux_ParseParam 482332 2477 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_ParseParam 1834873 657 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_ParseParam 23593393 51.0 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParseParam 2100160 574 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_ParseParam 4837220 252 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_ParseParam 18411192 66.2 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseParam 571870 2398 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_ParseParam 286262 4268 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_ParseParam 692906 2157 ns/op 992 B/op 15 allocs/op +BenchmarkPossum_ParseParam 1000000 1011 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_ParseParam 1722735 697 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_ParseParam 6058054 203 ns/op 48 B/op 1 allocs/op +BenchmarkTango_ParseParam 1000000 1061 ns/op 280 B/op 8 allocs/op +BenchmarkTigerTonic_ParseParam 890275 2277 ns/op 784 B/op 15 allocs/op +BenchmarkTraffic_ParseParam 351322 3543 ns/op 1896 B/op 21 allocs/op +BenchmarkVulcan_ParseParam 2076544 572 ns/op 98 B/op 3 allocs/op +BenchmarkAce_Parse2Params 11718074 101 ns/op 0 B/op 0 allocs/op +BenchmarkAero_Parse2Params 16264988 73.4 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Parse2Params 1238322 973 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_Parse2Params 1000000 1120 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Parse2Params 1000000 1632 ns/op 848 B/op 6 allocs/op +BenchmarkChi_Parse2Params 1239477 955 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_Parse2Params 4944133 245 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_Parse2Params 10518286 114 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Parse2Params 14505195 82.7 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Parse2Params 1000000 1437 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_Parse2Params 1689883 707 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Parse2Params 502334 2308 ns/op 1344 B/op 11 allocs/op +BenchmarkGoJsonRest_Parse2Params 1000000 1771 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_Parse2Params 159092 7583 ns/op 4928 B/op 14 allocs/op +BenchmarkGorillaMux_Parse2Params 417548 2980 ns/op 1296 B/op 10 allocs/op +BenchmarkGowwwRouter_Parse2Params 1751737 686 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Parse2Params 18089204 66.3 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_Parse2Params 1556986 777 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_Parse2Params 2493082 485 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_Parse2Params 15350108 78.5 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Parse2Params 530974 2605 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_Parse2Params 247069 4673 ns/op 1152 B/op 11 allocs/op +BenchmarkPat_Parse2Params 816295 2126 ns/op 752 B/op 16 allocs/op +BenchmarkPossum_Parse2Params 1000000 1002 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Parse2Params 1569771 733 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Parse2Params 4080546 295 ns/op 96 B/op 1 allocs/op +BenchmarkTango_Parse2Params 1000000 1121 ns/op 312 B/op 8 allocs/op +BenchmarkTigerTonic_Parse2Params 399556 3470 ns/op 1168 B/op 22 allocs/op +BenchmarkTraffic_Parse2Params 314194 4159 ns/op 1944 B/op 22 allocs/op +BenchmarkVulcan_Parse2Params 1827559 664 ns/op 98 B/op 3 allocs/op +BenchmarkAce_ParseAll 478395 2503 ns/op 0 B/op 0 allocs/op +BenchmarkAero_ParseAll 715392 1658 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseAll 59191 20124 ns/op 8928 B/op 110 allocs/op +BenchmarkBeego_ParseAll 45507 27266 ns/op 9152 B/op 78 allocs/op +BenchmarkBone_ParseAll 29328 41459 ns/op 16208 B/op 147 allocs/op +BenchmarkChi_ParseAll 48531 25053 ns/op 11232 B/op 78 allocs/op +BenchmarkDenco_ParseAll 325532 4284 ns/op 928 B/op 16 allocs/op +BenchmarkEcho_ParseAll 433771 2759 ns/op 0 B/op 0 allocs/op +BenchmarkGin_ParseAll 576316 2082 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseAll 41500 29692 ns/op 13728 B/op 181 allocs/op +BenchmarkGoji_ParseAll 80833 15563 ns/op 5376 B/op 32 allocs/op +BenchmarkGojiv2_ParseAll 19836 60335 ns/op 34448 B/op 277 allocs/op +BenchmarkGoJsonRest_ParseAll 32210 38027 ns/op 13866 B/op 321 allocs/op +BenchmarkGoRestful_ParseAll 6644 190842 ns/op 117600 B/op 354 allocs/op +BenchmarkGorillaMux_ParseAll 12634 95894 ns/op 30288 B/op 250 allocs/op +BenchmarkGowwwRouter_ParseAll 98152 12159 ns/op 6912 B/op 48 allocs/op +BenchmarkHttpRouter_ParseAll 933208 1273 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParseAll 107191 11554 ns/op 5728 B/op 51 allocs/op +BenchmarkKocha_ParseAll 184862 6225 ns/op 1112 B/op 54 allocs/op +BenchmarkLARS_ParseAll 644546 1858 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseAll 26145 46484 ns/op 19136 B/op 208 allocs/op +BenchmarkMartini_ParseAll 10000 121838 ns/op 25072 B/op 253 allocs/op +BenchmarkPat_ParseAll 25417 47196 ns/op 15216 B/op 308 allocs/op +BenchmarkPossum_ParseAll 58550 20735 ns/op 10816 B/op 78 allocs/op +BenchmarkR2router_ParseAll 72732 16584 ns/op 8352 B/op 120 allocs/op +BenchmarkRivet_ParseAll 281365 4968 ns/op 912 B/op 16 allocs/op +BenchmarkTango_ParseAll 42831 28668 ns/op 7168 B/op 208 allocs/op +BenchmarkTigerTonic_ParseAll 23774 49972 ns/op 16048 B/op 332 allocs/op +BenchmarkTraffic_ParseAll 10000 104679 ns/op 45520 B/op 605 allocs/op +BenchmarkVulcan_ParseAll 64810 18108 ns/op 2548 B/op 78 allocs/op ``` diff --git a/README.md b/README.md index dae63e24..d75843a7 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [AsciiJSON](#asciijson) - [PureJSON](#purejson) - [Serving static files](#serving-static-files) + - [Serving data from file](#serving-data-from-file) - [Serving data from reader](#serving-data-from-reader) - [HTML rendering](#html-rendering) - [Custom Template renderer](#custom-template-renderer) @@ -68,6 +69,8 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [Support Let's Encrypt](#support-lets-encrypt) - [Run multiple service using Gin](#run-multiple-service-using-gin) - [Graceful shutdown or restart](#graceful-shutdown-or-restart) + - [Third-party packages](#third-party-packages) + - [Manually](#manually) - [Build a single binary with templates](#build-a-single-binary-with-templates) - [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct) - [Try to bind body into different structs](#try-to-bind-body-into-different-structs) @@ -133,35 +136,38 @@ 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) | +| ------------------------------ | ---------:| ---------------:| ------------:| ---------------:| +| BenchmarkGin_GithubAll | **43550** | **27364 ns/op** | **0 B/op** | **0 allocs/op** | +| BenchmarkAce_GithubAll | 40543 | 29670 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkAero_GithubAll | 57632 | 20648 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBear_GithubAll | 9234 | 216179 ns/op | 86448 B/op | 943 allocs/op | +| BenchmarkBeego_GithubAll | 7407 | 243496 ns/op | 71456 B/op | 609 allocs/op | +| BenchmarkBone_GithubAll | 420 | 2922835 ns/op | 720160 B/op | 8620 allocs/op | +| BenchmarkChi_GithubAll | 7620 | 238331 ns/op | 87696 B/op | 609 allocs/op | +| BenchmarkDenco_GithubAll | 18355 | 64494 ns/op | 20224 B/op | 167 allocs/op | +| BenchmarkEcho_GithubAll | 31251 | 38479 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkGocraftWeb_GithubAll | 4117 | 300062 ns/op | 131656 B/op | 1686 allocs/op | +| BenchmarkGoji_GithubAll | 3274 | 416158 ns/op | 56112 B/op | 334 allocs/op | +| BenchmarkGojiv2_GithubAll | 1402 | 870518 ns/op | 352720 B/op | 4321 allocs/op | +| BenchmarkGoJsonRest_GithubAll | 2976 | 401507 ns/op | 134371 B/op | 2737 allocs/op | +| BenchmarkGoRestful_GithubAll | 410 | 2913158 ns/op | 910144 B/op | 2938 allocs/op | +| BenchmarkGorillaMux_GithubAll | 346 | 3384987 ns/op | 251650 B/op | 1994 allocs/op | +| BenchmarkGowwwRouter_GithubAll | 10000 | 143025 ns/op | 72144 B/op | 501 allocs/op | +| BenchmarkHttpRouter_GithubAll | 55938 | 21360 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHttpTreeMux_GithubAll | 10000 | 153944 ns/op | 65856 B/op | 671 allocs/op | +| BenchmarkKocha_GithubAll | 10000 | 106315 ns/op | 23304 B/op | 843 allocs/op | +| BenchmarkLARS_GithubAll | 47779 | 25084 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkMacaron_GithubAll | 3266 | 371907 ns/op | 149409 B/op | 1624 allocs/op | +| BenchmarkMartini_GithubAll | 331 | 3444706 ns/op | 226551 B/op | 2325 allocs/op | +| BenchmarkPat_GithubAll | 273 | 4381818 ns/op | 1483152 B/op | 26963 allocs/op | +| BenchmarkPossum_GithubAll | 10000 | 164367 ns/op | 84448 B/op | 609 allocs/op | +| BenchmarkR2router_GithubAll | 10000 | 160220 ns/op | 77328 B/op | 979 allocs/op | +| BenchmarkRivet_GithubAll | 14625 | 82453 ns/op | 16272 B/op | 167 allocs/op | +| BenchmarkTango_GithubAll | 6255 | 279611 ns/op | 63826 B/op | 1618 allocs/op | +| BenchmarkTigerTonic_GithubAll | 2008 | 687874 ns/op | 193856 B/op | 4474 allocs/op | +| BenchmarkTraffic_GithubAll | 355 | 3478508 ns/op | 820744 B/op | 14114 allocs/op | +| BenchmarkVulcan_GithubAll | 6885 | 193333 ns/op | 19894 B/op | 609 allocs/op | - (1): Total Repetitions achieved in constant time, higher means more confident result - (2): Single Repetition Duration (ns/op), lower is better From 235898e64285eb5c2cfdd485f21220e4deab3006 Mon Sep 17 00:00:00 2001 From: bn4t <17193640+bn4t@users.noreply.github.com> Date: Tue, 5 May 2020 15:20:00 +0000 Subject: [PATCH 85/90] Fix typo (#2358) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d75843a7..5c145d5a 100644 --- a/README.md +++ b/README.md @@ -1763,7 +1763,7 @@ func main() { // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit - log.Println("Shuting down server...") + log.Println("Shutting down server...") // The context is used to inform the server it has 5 seconds to finish // the request it is currently handling From 82b284fd774d6bf68c934a3f335b816c61edf66f Mon Sep 17 00:00:00 2001 From: Anup Kumar Panwar <1anuppanwar@gmail.com> Date: Fri, 8 May 2020 06:20:26 +0530 Subject: [PATCH 86/90] uncomment the code (#2362) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5c145d5a..9cc23fc2 100644 --- a/README.md +++ b/README.md @@ -357,14 +357,14 @@ References issue [#774](https://github.com/gin-gonic/gin/issues/774) and detail func main() { router := gin.Default() // Set a lower memory limit for multipart forms (default is 32 MiB) - // router.MaxMultipartMemory = 8 << 20 // 8 MiB + router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/upload", func(c *gin.Context) { // single file file, _ := c.FormFile("file") log.Println(file.Filename) // Upload the file to specific dst. - // c.SaveUploadedFile(file, dst) + c.SaveUploadedFile(file, dst) c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) }) @@ -388,7 +388,7 @@ See the detail [example code](https://github.com/gin-gonic/examples/tree/master/ func main() { router := gin.Default() // Set a lower memory limit for multipart forms (default is 32 MiB) - // router.MaxMultipartMemory = 8 << 20 // 8 MiB + router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/upload", func(c *gin.Context) { // Multipart form form, _ := c.MultipartForm() @@ -398,7 +398,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 6f3d96ccff56290fd506dff68af21a56a22d0873 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 9 May 2020 17:41:00 +0800 Subject: [PATCH 87/90] chore: improve render string performance (#2365) --- render/text.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/render/text.go b/render/text.go index 4e52d4c5..30f5f532 100644 --- a/render/text.go +++ b/render/text.go @@ -6,7 +6,6 @@ package render import ( "fmt" - "io" "net/http" ) @@ -35,6 +34,6 @@ func WriteString(w http.ResponseWriter, format string, data []interface{}) (err _, err = fmt.Fprintf(w, format, data...) return } - _, err = io.WriteString(w, format) + _, err = w.Write([]byte(format)) return } From d17270dd90c488308de3102d6951946ca0a5911f Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sun, 10 May 2020 13:22:25 +0800 Subject: [PATCH 88/90] Sync route tree to httprouter latest code (#2368) * update tree Signed-off-by: Bo-Yi Wu * update Signed-off-by: Bo-Yi Wu * update Signed-off-by: Bo-Yi Wu * update countParams Signed-off-by: Bo-Yi Wu * fix testing Signed-off-by: Bo-Yi Wu * update Signed-off-by: Bo-Yi Wu * update Signed-off-by: Bo-Yi Wu * udpate Signed-off-by: Bo-Yi Wu * fix testing Signed-off-by: Bo-Yi Wu * refactor gin context Signed-off-by: Bo-Yi Wu * add fullPath Signed-off-by: Bo-Yi Wu * chore: refactor * remove unused code Signed-off-by: Bo-Yi Wu * remove varsCount Signed-off-by: Bo-Yi Wu * refactor Signed-off-by: Bo-Yi Wu --- context.go | 2 + gin.go | 16 +- tree.go | 609 +++++++++++++++++++++++++++++---------------------- tree_test.go | 84 ++++--- 4 files changed, 411 insertions(+), 300 deletions(-) diff --git a/context.go b/context.go index 4ebcc294..2ea1a028 100644 --- a/context.go +++ b/context.go @@ -54,6 +54,7 @@ type Context struct { fullPath string engine *Engine + params *Params // This mutex protect Keys map mu sync.RWMutex @@ -95,6 +96,7 @@ func (c *Context) reset() { c.Accepted = nil c.queryCache = nil c.formCache = nil + *c.params = (*c.params)[0:0] } // Copy returns a copy of the current context that can be safely used outside the request's scope. diff --git a/gin.go b/gin.go index 4e72f81d..1e126179 100644 --- a/gin.go +++ b/gin.go @@ -113,6 +113,7 @@ type Engine struct { noMethod HandlersChain pool sync.Pool trees methodTrees + maxParams uint16 } var _ IRouter = &Engine{} @@ -163,7 +164,8 @@ func Default() *Engine { } func (engine *Engine) allocateContext() *Context { - return &Context{engine: engine} + v := make(Params, 0, engine.maxParams) + return &Context{engine: engine, params: &v} } // Delims sets template left and right delims and returns a Engine instance. @@ -256,6 +258,7 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { assert1(len(handlers) > 0, "there must be at least one handler") debugPrintRoute(method, path, handlers) + root := engine.trees.get(method) if root == nil { root = new(node) @@ -263,6 +266,11 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { engine.trees = append(engine.trees, methodTree{method: method, root: root}) } root.addRoute(path, handlers) + + // Update maxParams + if paramsCount := countParams(path); paramsCount > engine.maxParams { + engine.maxParams = paramsCount + } } // Routes returns a slice of registered routes, including some useful information, such as: @@ -402,10 +410,12 @@ func (engine *Engine) handleHTTPRequest(c *Context) { } root := t[i].root // Find route in tree - value := root.getValue(rPath, c.Params, unescape) + value := root.getValue(rPath, c.params, unescape) + if value.params != nil { + c.Params = *value.params + } if value.handlers != nil { c.handlers = value.handlers - c.Params = value.params c.fullPath = value.fullPath c.Next() c.writermem.WriteHeaderNow() diff --git a/tree.go b/tree.go index b687ec43..e3aa9190 100644 --- a/tree.go +++ b/tree.go @@ -8,6 +8,7 @@ import ( "net/url" "strings" "unicode" + "unicode/utf8" ) // Param is a single URL parameter, consisting of a key and a value. @@ -71,17 +72,15 @@ func longestCommonPrefix(a, b string) int { return i } -func countParams(path string) uint8 { +func countParams(path string) uint16 { var n uint - for i := 0; i < len(path); i++ { - if path[i] == ':' || path[i] == '*' { + for i := range []byte(path) { + switch path[i] { + case ':', '*': n++ } } - if n >= 255 { - return 255 - } - return uint8(n) + return uint16(n) } type nodeType uint8 @@ -96,16 +95,15 @@ const ( type node struct { path string indices string + wildChild bool + nType nodeType + priority uint32 children []*node handlers HandlersChain - priority uint32 - nType nodeType - maxParams uint8 - wildChild bool fullPath string } -// 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 { cs := n.children cs[pos].priority++ @@ -116,13 +114,14 @@ func (n *node) incrementChildPrio(pos int) int { for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- { // Swap node positions cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1] + } - // build new index char string + // Build new index char string if newPos != pos { - n.indices = n.indices[:newPos] + // unchanged prefix, might be empty - n.indices[pos:pos+1] + // the index char we move - n.indices[newPos:pos] + n.indices[pos+1:] // rest without char at 'pos' + n.indices = n.indices[:newPos] + // Unchanged prefix, might be empty + n.indices[pos:pos+1] + // The index char we move + n.indices[newPos:pos] + n.indices[pos+1:] // Rest without char at 'pos' } return newPos @@ -133,11 +132,10 @@ func (n *node) incrementChildPrio(pos int) int { func (n *node) addRoute(path string, handlers HandlersChain) { fullPath := path n.priority++ - numParams := countParams(path) // Empty tree if len(n.path) == 0 && len(n.children) == 0 { - n.insertChild(numParams, path, fullPath, handlers) + n.insertChild(path, fullPath, handlers) n.nType = root return } @@ -146,11 +144,6 @@ func (n *node) addRoute(path string, handlers HandlersChain) { walk: for { - // Update maxParams of the current node - if numParams > n.maxParams { - n.maxParams = numParams - } - // Find the longest common prefix. // This also implies that the common prefix contains no ':' or '*' // since the existing key can't contain those chars. @@ -168,13 +161,6 @@ walk: fullPath: n.fullPath, } - // Update maxParams (max of all children) - for _, v := range child.children { - if v.maxParams > child.maxParams { - child.maxParams = v.maxParams - } - } - n.children = []*node{&child} // []byte for proper unicode char conversion, see #65 n.indices = string([]byte{n.path[i]}) @@ -193,18 +179,13 @@ walk: n = n.children[0] n.priority++ - // Update maxParams of the child node - if numParams > n.maxParams { - n.maxParams = numParams - } - numParams-- - // Check if the wildcard matches - if len(path) >= len(n.path) && n.path == path[:len(n.path)] { - // check for longer wildcard, e.g. :name and :names - if len(n.path) >= len(path) || path[len(n.path)] == '/' { - continue walk - } + if len(path) >= len(n.path) && n.path == path[:len(n.path)] && + // Adding a child to a catchAll is not possible + n.nType != catchAll && + // Check for longer wildcard, e.g. :name and :names + (len(n.path) >= len(path) || path[len(n.path)] == '/') { + continue walk } pathSeg := path @@ -244,14 +225,13 @@ walk: // []byte for proper unicode char conversion, see #65 n.indices += string([]byte{c}) child := &node{ - maxParams: numParams, - fullPath: fullPath, + fullPath: fullPath, } n.children = append(n.children, child) n.incrementChildPrio(len(n.indices) - 1) n = child } - n.insertChild(numParams, path, fullPath, handlers) + n.insertChild(path, fullPath, handlers) return } @@ -265,7 +245,7 @@ walk: } // Search for a wildcard segment and check the name for invalid characters. -// Returns -1 as index, if no wildcard war found. +// Returns -1 as index, if no wildcard was found. func findWildcard(path string) (wildcard string, i int, valid bool) { // Find start for start, c := range []byte(path) { @@ -289,8 +269,8 @@ func findWildcard(path string) (wildcard string, i int, valid bool) { return "", -1, false } -func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) { - for numParams > 0 { +func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) { + for { // Find prefix until first wildcard wildcard, i, valid := findWildcard(path) if i < 0 { // No wildcard found @@ -324,15 +304,13 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle n.wildChild = true child := &node{ - nType: param, - path: wildcard, - maxParams: numParams, - fullPath: fullPath, + nType: param, + path: wildcard, + fullPath: fullPath, } n.children = []*node{child} n = child n.priority++ - numParams-- // if the path doesn't end with the wildcard, then there // will be another non-wildcard subpath starting with '/' @@ -340,9 +318,8 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle path = path[len(wildcard):] child := &node{ - maxParams: numParams, - priority: 1, - fullPath: fullPath, + priority: 1, + fullPath: fullPath, } n.children = []*node{child} n = child @@ -355,7 +332,7 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle } // catchAll - if i+len(wildcard) != len(path) || numParams > 1 { + if i+len(wildcard) != len(path) { panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'") } @@ -375,13 +352,9 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle child := &node{ wildChild: true, nType: catchAll, - maxParams: 1, fullPath: fullPath, } - // update maxParams of the parent node - if n.maxParams < 1 { - n.maxParams = 1 - } + n.children = []*node{child} n.indices = string('/') n = child @@ -389,12 +362,11 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle // second node: node holding the variable child = &node{ - path: path[i:], - nType: catchAll, - maxParams: 1, - handlers: handlers, - priority: 1, - fullPath: fullPath, + path: path[i:], + nType: catchAll, + handlers: handlers, + priority: 1, + fullPath: fullPath, } n.children = []*node{child} @@ -410,21 +382,128 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle // nodeValue holds return values of (*Node).getValue method type nodeValue struct { handlers HandlersChain - params Params + params *Params tsr bool fullPath string } -// getValue returns the handle registered with the given path (key). The values of +// Returns the handle registered with the given path (key). The values of // wildcards are saved to a map. // If no handle can be found, a TSR (trailing slash redirect) recommendation is // made if a handle exists with an extra (without the) trailing slash for the // given path. -func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue) { - value.params = po +func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) { walk: // Outer loop for walking the tree for { prefix := n.path + if len(path) > len(prefix) { + if path[:len(prefix)] == prefix { + path = path[len(prefix):] + // If this node does not have a wildcard (param or catchAll) + // child, we can just look up the next child node and continue + // to walk down the tree + if !n.wildChild { + idxc := path[0] + for i, c := range []byte(n.indices) { + if c == idxc { + n = n.children[i] + continue walk + } + } + + // Nothing found. + // We can recommend to redirect to the same URL without a + // trailing slash if a leaf exists for that path. + value.tsr = (path == "/" && n.handlers != nil) + return + } + + // Handle wildcard child + n = n.children[0] + switch n.nType { + case param: + // Find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ + } + + // Save param value + if params != nil { + if value.params == nil { + value.params = params + } + // Expand slice within preallocated capacity + i := len(*value.params) + *value.params = (*value.params)[:i+1] + val := path[:end] + if unescape { + if v, err := url.QueryUnescape(val); err == nil { + val = v + } + } + (*value.params)[i] = Param{ + Key: n.path[1:], + Value: val, + } + } + + // we need to go deeper! + if end < len(path) { + if len(n.children) > 0 { + path = path[end:] + n = n.children[0] + continue walk + } + + // ... but we can't + value.tsr = (len(path) == end+1) + return + } + + if value.handlers = n.handlers; value.handlers != nil { + value.fullPath = n.fullPath + return + } + if len(n.children) == 1 { + // No handle found. Check if a handle for this path + a + // trailing slash exists for TSR recommendation + n = n.children[0] + value.tsr = (n.path == "/" && n.handlers != nil) + } + return + + case catchAll: + // Save param value + if params != nil { + if value.params == nil { + value.params = params + } + // Expand slice within preallocated capacity + i := len(*value.params) + *value.params = (*value.params)[:i+1] + val := path + if unescape { + if v, err := url.QueryUnescape(path); err == nil { + val = v + } + } + (*value.params)[i] = Param{ + Key: n.path[2:], + Value: val, + } + } + + value.handlers = n.handlers + value.fullPath = n.fullPath + return + + default: + panic("invalid node type") + } + } + } + if path == prefix { // We should have reached the node containing the handle. // Check if this node has a handle registered. @@ -433,6 +512,9 @@ walk: // Outer loop for walking the tree return } + // If there is no handle for this route, but this route has a + // wildcard child, there must be a handle for this path with an + // additional trailing slash if path == "/" && n.wildChild && n.nType != root { value.tsr = true return @@ -440,9 +522,8 @@ walk: // Outer loop for walking the tree // No handle found. Check if a handle for this path + a // trailing slash exists for trailing slash recommendation - indices := n.indices - for i, max := 0, len(indices); i < max; i++ { - if indices[i] == '/' { + for i, c := range []byte(n.indices) { + if c == '/' { n = n.children[i] value.tsr = (len(n.path) == 1 && n.handlers != nil) || (n.nType == catchAll && n.children[0].handlers != nil) @@ -453,106 +534,6 @@ walk: // Outer loop for walking the tree return } - if len(path) > len(prefix) && path[:len(prefix)] == prefix { - path = path[len(prefix):] - // If this node does not have a wildcard (param or catchAll) - // child, we can just look up the next child node and continue - // to walk down the tree - if !n.wildChild { - c := path[0] - indices := n.indices - for i, max := 0, len(indices); i < max; i++ { - if c == indices[i] { - n = n.children[i] - continue walk - } - } - - // Nothing found. - // We can recommend to redirect to the same URL without a - // trailing slash if a leaf exists for that path. - value.tsr = path == "/" && n.handlers != nil - return - } - - // handle wildcard child - n = n.children[0] - switch n.nType { - case param: - // find param end (either '/' or path end) - end := 0 - for end < len(path) && path[end] != '/' { - end++ - } - - // save param value - if cap(value.params) < int(n.maxParams) { - value.params = make(Params, 0, n.maxParams) - } - i := len(value.params) - value.params = value.params[:i+1] // expand slice within preallocated capacity - value.params[i].Key = n.path[1:] - val := path[:end] - if unescape { - var err error - if value.params[i].Value, err = url.QueryUnescape(val); err != nil { - value.params[i].Value = val // fallback, in case of error - } - } else { - value.params[i].Value = val - } - - // we need to go deeper! - if end < len(path) { - if len(n.children) > 0 { - path = path[end:] - n = n.children[0] - continue walk - } - - // ... but we can't - value.tsr = len(path) == end+1 - return - } - - if value.handlers = n.handlers; value.handlers != nil { - value.fullPath = n.fullPath - return - } - if len(n.children) == 1 { - // No handle found. Check if a handle for this path + a - // trailing slash exists for TSR recommendation - n = n.children[0] - value.tsr = n.path == "/" && n.handlers != nil - } - return - - case catchAll: - // save param value - if cap(value.params) < int(n.maxParams) { - value.params = make(Params, 0, n.maxParams) - } - i := len(value.params) - value.params = value.params[:i+1] // expand slice within preallocated capacity - value.params[i].Key = n.path[2:] - if unescape { - var err error - if value.params[i].Value, err = url.QueryUnescape(path); err != nil { - value.params[i].Value = path // fallback, in case of error - } - } else { - value.params[i].Value = path - } - - value.handlers = n.handlers - value.fullPath = n.fullPath - return - - default: - panic("invalid node type") - } - } - // Nothing found. We can recommend to redirect to the same URL with an // extra trailing slash if a leaf exists for that path value.tsr = (path == "/") || @@ -562,109 +543,214 @@ walk: // Outer loop for walking the tree } } -// findCaseInsensitivePath makes a case-insensitive lookup of the given path and tries to find a handler. +// 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. -func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPath []byte, found bool) { - ciPath = make([]byte, 0, len(path)+1) // preallocate enough memory +func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) ([]byte, bool) { + const stackBufSize = 128 - // Outer loop for walking the tree - for len(path) >= len(n.path) && strings.EqualFold(path[:len(n.path)], n.path) { - path = path[len(n.path):] + // Use a static sized buffer on the stack in the common case. + // If the path is too long, allocate a buffer on the heap instead. + buf := make([]byte, 0, stackBufSize) + if l := len(path) + 1; l > stackBufSize { + buf = make([]byte, 0, l) + } + + ciPath := n.findCaseInsensitivePathRec( + path, + buf, // Preallocate enough memory for new path + [4]byte{}, // Empty rune buffer + fixTrailingSlash, + ) + + return ciPath, ciPath != nil +} + +// Shift bytes in array by n bytes left +func shiftNRuneBytes(rb [4]byte, n int) [4]byte { + switch n { + case 0: + return rb + case 1: + return [4]byte{rb[1], rb[2], rb[3], 0} + case 2: + return [4]byte{rb[2], rb[3]} + case 3: + return [4]byte{rb[3]} + default: + return [4]byte{} + } +} + +// Recursive case-insensitive lookup function used by n.findCaseInsensitivePath +func (n *node) findCaseInsensitivePathRec(path string, ciPath []byte, rb [4]byte, fixTrailingSlash bool) []byte { + npLen := len(n.path) + +walk: // Outer loop for walking the tree + for len(path) >= npLen && (npLen == 0 || strings.EqualFold(path[1:npLen], n.path[1:])) { + // Add common prefix to result + oldPath := path + path = path[npLen:] ciPath = append(ciPath, n.path...) - if len(path) == 0 { + if len(path) > 0 { + // If this node does not have a wildcard (param or catchAll) child, + // we can just look up the next child node and continue to walk down + // the tree + if !n.wildChild { + // Skip rune bytes already processed + rb = shiftNRuneBytes(rb, npLen) + + if rb[0] != 0 { + // Old rune not finished + idxc := rb[0] + for i, c := range []byte(n.indices) { + if c == idxc { + // continue with child node + n = n.children[i] + npLen = len(n.path) + continue walk + } + } + } else { + // Process a new rune + var rv rune + + // Find rune start. + // Runes are up to 4 byte long, + // -4 would definitely be another rune. + var off int + for max := min(npLen, 3); off < max; off++ { + if i := npLen - off; utf8.RuneStart(oldPath[i]) { + // read rune from cached path + rv, _ = utf8.DecodeRuneInString(oldPath[i:]) + break + } + } + + // Calculate lowercase bytes of current rune + lo := unicode.ToLower(rv) + utf8.EncodeRune(rb[:], lo) + + // Skip already processed bytes + rb = shiftNRuneBytes(rb, off) + + idxc := rb[0] + for i, c := range []byte(n.indices) { + // Lowercase matches + if c == idxc { + // must use a recursive approach since both the + // uppercase byte and the lowercase byte might exist + // as an index + if out := n.children[i].findCaseInsensitivePathRec( + path, ciPath, rb, fixTrailingSlash, + ); out != nil { + return out + } + break + } + } + + // If we found no match, the same for the uppercase rune, + // if it differs + if up := unicode.ToUpper(rv); up != lo { + utf8.EncodeRune(rb[:], up) + rb = shiftNRuneBytes(rb, off) + + idxc := rb[0] + for i, c := range []byte(n.indices) { + // Uppercase matches + if c == idxc { + // Continue with child node + n = n.children[i] + npLen = len(n.path) + continue walk + } + } + } + } + + // Nothing found. We can recommend to redirect to the same URL + // without a trailing slash if a leaf exists for that path + if fixTrailingSlash && path == "/" && n.handlers != nil { + return ciPath + } + return nil + } + + n = n.children[0] + switch n.nType { + case param: + // Find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ + } + + // Add param value to case insensitive path + ciPath = append(ciPath, path[:end]...) + + // We need to go deeper! + if end < len(path) { + if len(n.children) > 0 { + // Continue with child node + n = n.children[0] + npLen = len(n.path) + path = path[end:] + continue + } + + // ... but we can't + if fixTrailingSlash && len(path) == end+1 { + return ciPath + } + return nil + } + + if n.handlers != nil { + return ciPath + } + + if fixTrailingSlash && len(n.children) == 1 { + // No handle found. Check if a handle for this path + a + // trailing slash exists + n = n.children[0] + if n.path == "/" && n.handlers != nil { + return append(ciPath, '/') + } + } + + return nil + + case catchAll: + return append(ciPath, path...) + + default: + panic("invalid node type") + } + } else { // We should have reached the node containing the handle. // Check if this node has a handle registered. if n.handlers != nil { - return ciPath, true + return ciPath } // No handle found. // Try to fix the path by adding a trailing slash if fixTrailingSlash { - for i := 0; i < len(n.indices); i++ { - if n.indices[i] == '/' { + for i, c := range []byte(n.indices) { + if c == '/' { n = n.children[i] if (len(n.path) == 1 && n.handlers != nil) || (n.nType == catchAll && n.children[0].handlers != nil) { - return append(ciPath, '/'), true + return append(ciPath, '/') } - return + return nil } } } - return - } - - // If this node does not have a wildcard (param or catchAll) child, - // we can just look up the next child node and continue to walk down - // the tree - if !n.wildChild { - r := unicode.ToLower(rune(path[0])) - for i, index := range n.indices { - // must use recursive approach since both index and - // ToLower(index) could exist. We must check both. - if r == unicode.ToLower(index) { - out, found := n.children[i].findCaseInsensitivePath(path, fixTrailingSlash) - if found { - return append(ciPath, out...), true - } - } - } - - // Nothing found. We can recommend to redirect to the same URL - // without a trailing slash if a leaf exists for that path - found = fixTrailingSlash && path == "/" && n.handlers != nil - return - } - - n = n.children[0] - switch n.nType { - case param: - // Find param end (either '/' or path end) - end := 0 - for end < len(path) && path[end] != '/' { - end++ - } - - // add param value to case insensitive path - ciPath = append(ciPath, path[:end]...) - - // we need to go deeper! - if end < len(path) { - if len(n.children) > 0 { - path = path[end:] - n = n.children[0] - continue - } - - // ... but we can't - if fixTrailingSlash && len(path) == end+1 { - return ciPath, true - } - return - } - - if n.handlers != nil { - return ciPath, true - } - if fixTrailingSlash && len(n.children) == 1 { - // No handle found. Check if a handle for this path + a - // trailing slash exists - n = n.children[0] - if n.path == "/" && n.handlers != nil { - return append(ciPath, '/'), true - } - } - return - - case catchAll: - return append(ciPath, path...), true - - default: - panic("invalid node type") + return nil } } @@ -672,13 +758,12 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa // Try to fix the path by adding / removing a trailing slash if fixTrailingSlash { if path == "/" { - return ciPath, true + return ciPath } - if len(path)+1 == len(n.path) && n.path[len(path)] == '/' && - strings.EqualFold(path, n.path[:len(path)]) && - n.handlers != nil { - return append(ciPath, n.path...), true + if len(path)+1 == npLen && n.path[len(path)] == '/' && + strings.EqualFold(path[1:], n.path[1:len(path)]) && n.handlers != nil { + return append(ciPath, n.path...) } } - return + return nil } diff --git a/tree_test.go b/tree_test.go index 0fe2fe00..1cb4f559 100644 --- a/tree_test.go +++ b/tree_test.go @@ -28,6 +28,11 @@ type testRequests []struct { ps Params } +func getParams() *Params { + ps := make(Params, 0, 20) + return &ps +} + func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ...bool) { unescape := false if len(unescapes) >= 1 { @@ -35,7 +40,7 @@ func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes .. } for _, request := range requests { - value := tree.getValue(request.path, nil, unescape) + value := tree.getValue(request.path, getParams(), unescape) if value.handlers == nil { if !request.nilHandler { @@ -50,9 +55,12 @@ func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes .. } } - if !reflect.DeepEqual(value.params, request.ps) { - t.Errorf("Params mismatch for route '%s'", request.path) + if value.params != nil { + if !reflect.DeepEqual(*value.params, request.ps) { + t.Errorf("Params mismatch for route '%s'", request.path) + } } + } } @@ -76,33 +84,11 @@ func checkPriorities(t *testing.T, n *node) uint32 { return prio } -func checkMaxParams(t *testing.T, n *node) uint8 { - var maxParams uint8 - for i := range n.children { - params := checkMaxParams(t, n.children[i]) - if params > maxParams { - maxParams = params - } - } - if n.nType > root && !n.wildChild { - maxParams++ - } - - if n.maxParams != maxParams { - t.Errorf( - "maxParams mismatch for node '%s': is %d, should be %d", - n.path, n.maxParams, maxParams, - ) - } - - return maxParams -} - func TestCountParams(t *testing.T) { if countParams("/path/:param1/static/*catch-all") != 2 { t.Fail() } - if countParams(strings.Repeat("/:param", 256)) != 255 { + if countParams(strings.Repeat("/:param", 256)) != 256 { t.Fail() } } @@ -142,7 +128,6 @@ func TestTreeAddAndGet(t *testing.T) { }) checkPriorities(t, tree) - checkMaxParams(t, tree) } func TestTreeWildcard(t *testing.T) { @@ -186,7 +171,6 @@ func TestTreeWildcard(t *testing.T) { }) checkPriorities(t, tree) - checkMaxParams(t, tree) } func TestUnescapeParameters(t *testing.T) { @@ -224,7 +208,6 @@ func TestUnescapeParameters(t *testing.T) { }, unescape) checkPriorities(t, tree) - checkMaxParams(t, tree) } func catchPanic(testFunc func()) (recv interface{}) { @@ -323,12 +306,14 @@ func TestTreeDupliatePath(t *testing.T) { } } + //printChildren(tree, "") + checkRequests(t, tree, testRequests{ {"/", false, "/", nil}, {"/doc/", false, "/doc/", nil}, - {"/src/some/file.png", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/some/file.png"}}}, - {"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{Key: "query", Value: "someth!ng+in+ünìcodé"}}}, - {"/user_gopher", false, "/user_:name", Params{Param{Key: "name", Value: "gopher"}}}, + {"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}}, + {"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}}, + {"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}}, }) } @@ -356,6 +341,8 @@ func TestTreeCatchAllConflict(t *testing.T) { {"/src/*filepath/x", true}, {"/src2/", false}, {"/src2/*filepath/x", true}, + {"/src3/*filepath", false}, + {"/src3/*filepath/x", true}, } testRoutes(t, routes) } @@ -372,7 +359,6 @@ func TestTreeCatchMaxParams(t *testing.T) { tree := &node{} var route = "/cmd/*filepath" tree.addRoute(route, fakeHandler(route)) - checkMaxParams(t, tree) } func TestTreeDoubleWildcard(t *testing.T) { @@ -508,6 +494,9 @@ func TestTreeRootTrailingSlashRedirect(t *testing.T) { func TestTreeFindCaseInsensitivePath(t *testing.T) { tree := &node{} + longPath := "/l" + strings.Repeat("o", 128) + "ng" + lOngPath := "/l" + strings.Repeat("O", 128) + "ng/" + routes := [...]string{ "/hi", "/b/", @@ -531,6 +520,17 @@ func TestTreeFindCaseInsensitivePath(t *testing.T) { "/doc/go/away", "/no/a", "/no/b", + "/Π", + "/u/apfêl/", + "/u/äpfêl/", + "/u/öpfêl", + "/v/Äpfêl/", + "/v/Öpfêl", + "/w/♬", // 3 byte + "/w/♭/", // 3 byte, last byte differs + "/w/𠜎", // 4 byte + "/w/𠜏/", // 4 byte + longPath, } for _, route := range routes { @@ -609,6 +609,21 @@ func TestTreeFindCaseInsensitivePath(t *testing.T) { {"/DOC/", "/doc", true, true}, {"/NO", "", false, true}, {"/DOC/GO", "", false, true}, + {"/π", "/Π", true, false}, + {"/π/", "/Π", true, true}, + {"/u/ÄPFÊL/", "/u/äpfêl/", true, false}, + {"/u/ÄPFÊL", "/u/äpfêl/", true, true}, + {"/u/ÖPFÊL/", "/u/öpfêl", true, true}, + {"/u/ÖPFÊL", "/u/öpfêl", true, false}, + {"/v/äpfêL/", "/v/Äpfêl/", true, false}, + {"/v/äpfêL", "/v/Äpfêl/", true, true}, + {"/v/öpfêL/", "/v/Öpfêl", true, true}, + {"/v/öpfêL", "/v/Öpfêl", true, false}, + {"/w/♬/", "/w/♬", true, true}, + {"/w/♭", "/w/♭/", true, true}, + {"/w/𠜎/", "/w/𠜎", true, true}, + {"/w/𠜏", "/w/𠜏/", true, true}, + {lOngPath, longPath, true, true}, } // With fixTrailingSlash = true for _, test := range tests { @@ -696,8 +711,7 @@ func TestTreeWildcardConflictEx(t *testing.T) { tree.addRoute(conflict.route, fakeHandler(conflict.route)) }) - if !regexp.MustCompile(fmt.Sprintf("'%s' in new path .* conflicts with existing wildcard '%s' in existing prefix '%s'", - conflict.segPath, conflict.existSegPath, conflict.existPath)).MatchString(fmt.Sprint(recv)) { + if !regexp.MustCompile(fmt.Sprintf("'%s' in new path .* conflicts with existing wildcard '%s' in existing prefix '%s'", conflict.segPath, conflict.existSegPath, conflict.existPath)).MatchString(fmt.Sprint(recv)) { t.Fatalf("invalid wildcard conflict error (%v)", recv) } } From a6e8665e42dad83ee275d4adc34a6e507bdd11ed Mon Sep 17 00:00:00 2001 From: vinhha96 Date: Mon, 11 May 2020 12:25:49 +0700 Subject: [PATCH 89/90] fix(tree): reassign fullpath when register new node which the same current node (#2366) * fix(tree): assign fullpath to current node by fullpath of new node if current node the same new node * test(router-test): reverse the order when register router when test func GetFullPath * chg(tree-test): update test case with register new route in TestRouteContextHoldsFullPath Co-authored-by: vinhha Co-authored-by: Bo-Yi Wu --- routes_test.go | 3 +++ tree.go | 1 + 2 files changed, 4 insertions(+) diff --git a/routes_test.go b/routes_test.go index 360ade62..11ff71a6 100644 --- a/routes_test.go +++ b/routes_test.go @@ -593,6 +593,9 @@ func TestRouteContextHoldsFullPath(t *testing.T) { "/simple-two/one-two", "/project/:name/build/*params", "/project/:name/bui", + "/user/:id/status", + "/user/:id", + "/user/:id/profile", } for _, route := range routes { diff --git a/tree.go b/tree.go index e3aa9190..f528fa3b 100644 --- a/tree.go +++ b/tree.go @@ -240,6 +240,7 @@ walk: panic("handlers are already registered for path '" + fullPath + "'") } n.handlers = handlers + n.fullPath = fullPath return } } From 1d5b9badd97ba578f00a0d45ad46834cca5479ca Mon Sep 17 00:00:00 2001 From: thinkerou Date: Thu, 14 May 2020 11:35:14 +0800 Subject: [PATCH 90/90] chore: rename getQueryCache/getFormCache to initQueryCache/initFormCache (#2375) --- context.go | 12 ++++++------ utils.go | 13 ++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/context.go b/context.go index 2ea1a028..a9458833 100644 --- a/context.go +++ b/context.go @@ -414,7 +414,7 @@ func (c *Context) QueryArray(key string) []string { return values } -func (c *Context) getQueryCache() { +func (c *Context) initQueryCache() { if c.queryCache == nil { c.queryCache = c.Request.URL.Query() } @@ -423,7 +423,7 @@ func (c *Context) getQueryCache() { // 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) { - c.getQueryCache() + c.initQueryCache() if values, ok := c.queryCache[key]; ok && len(values) > 0 { return values, true } @@ -439,7 +439,7 @@ func (c *Context) QueryMap(key string) map[string]string { // GetQueryMap returns a map for a given query key, plus a boolean value // whether at least one value exists for the given key. func (c *Context) GetQueryMap(key string) (map[string]string, bool) { - c.getQueryCache() + c.initQueryCache() return c.get(c.queryCache, key) } @@ -481,7 +481,7 @@ func (c *Context) PostFormArray(key string) []string { return values } -func (c *Context) getFormCache() { +func (c *Context) initFormCache() { if c.formCache == nil { c.formCache = make(url.Values) req := c.Request @@ -497,7 +497,7 @@ func (c *Context) getFormCache() { // GetPostFormArray returns a slice of strings for a given form key, plus // a boolean value whether at least one value exists for the given key. func (c *Context) GetPostFormArray(key string) ([]string, bool) { - c.getFormCache() + c.initFormCache() if values := c.formCache[key]; len(values) > 0 { return values, true } @@ -513,7 +513,7 @@ func (c *Context) PostFormMap(key string) map[string]string { // GetPostFormMap returns a map for a given form key, plus a boolean value // whether at least one value exists for the given key. func (c *Context) GetPostFormMap(key string) (map[string]string, bool) { - c.getFormCache() + c.initFormCache() return c.get(c.formCache, key) } diff --git a/utils.go b/utils.go index 71b80de7..fab3aee3 100644 --- a/utils.go +++ b/utils.go @@ -90,13 +90,13 @@ func filterFlags(content string) string { } func chooseData(custom, wildcard interface{}) interface{} { - if custom == nil { - if wildcard == nil { - panic("negotiation config is invalid") - } + if custom != nil { + return custom + } + if wildcard != nil { return wildcard } - return custom + panic("negotiation config is invalid") } func parseAccept(acceptHeader string) []string { @@ -127,8 +127,7 @@ func joinPaths(absolutePath, relativePath string) string { } finalPath := path.Join(absolutePath, relativePath) - appendSlash := lastChar(relativePath) == '/' && lastChar(finalPath) != '/' - if appendSlash { + if lastChar(relativePath) == '/' && lastChar(finalPath) != '/' { return finalPath + "/" } return finalPath