mirror of https://github.com/gin-gonic/gin.git
Merge branch 'master' into master
This commit is contained in:
commit
a5e7ac0831
1267
BENCHMARKS.md
1267
BENCHMARKS.md
File diff suppressed because it is too large
Load Diff
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -1,3 +1,18 @@
|
|||
# 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
|
||||
* 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
|
||||
|
|
66
README.md
66
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)
|
||||
|
@ -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
|
||||
|
|
5
auth.go
5
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)
|
||||
|
|
33
context.go
33
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,
|
||||
|
@ -54,7 +56,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 +88,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 +100,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 +235,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
|
||||
}
|
||||
|
||||
|
@ -866,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.
|
||||
|
|
17
gin.go
17
gin.go
|
@ -23,11 +23,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)
|
||||
|
||||
|
@ -106,7 +107,7 @@ type Engine struct {
|
|||
RemoveExtraSlash bool
|
||||
|
||||
delims render.Delims
|
||||
secureJsonPrefix string
|
||||
secureJSONPrefix string
|
||||
HTMLRender render.HTMLRender
|
||||
FuncMap template.FuncMap
|
||||
allNoRoute HandlersChain
|
||||
|
@ -147,7 +148,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{} {
|
||||
|
@ -165,7 +166,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.
|
||||
|
@ -174,9 +175,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
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
package json
|
||||
|
||||
import "github.com/json-iterator/go"
|
||||
import jsoniter "github.com/json-iterator/go"
|
||||
|
||||
var (
|
||||
json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
|
|
1
mode.go
1
mode.go
|
@ -22,6 +22,7 @@ const (
|
|||
// TestMode indicates gin mode is test.
|
||||
TestMode = "test"
|
||||
)
|
||||
|
||||
const (
|
||||
debugCode = iota
|
||||
releaseCode
|
||||
|
|
7
path.go
7
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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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/")))
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
package gin
|
||||
|
||||
// Version is the current gin framework's version.
|
||||
const Version = "v1.6.2"
|
||||
const Version = "v1.6.3"
|
||||
|
|
Loading…
Reference in New Issue