remove ccirrelo/supervisor, update

everything seems to work even though sirupsen is upper case?

:cyfap:
This commit is contained in:
Reed Allman
2017-09-05 11:36:47 -07:00
parent 78ba35fb23
commit 27e43c5d94
397 changed files with 13691 additions and 8828 deletions

View File

@@ -4,6 +4,7 @@ go:
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9
- master
git:

View File

@@ -40,7 +40,7 @@ $ go run example.go
## Benchmarks
Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter)
Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter)
[See all benchmarks](/BENCHMARKS.md)
@@ -74,10 +74,10 @@ BenchmarkTigerTonic_GithubAll | 1000 | 1439483 | 239104
BenchmarkTraffic_GithubAll | 100 | 11383067 | 2659329 | 21848
BenchmarkVulcan_GithubAll | 5000 | 394253 | 19894 | 609
(1): Total Repetitions achieved in constant time, higher means more confident result
(2): Single Repetition Duration (ns/op), lower is better
(3): Heap Memory (B/op), lower is better
(4): Average Allocations per Repetition (allocs/op), lower is better
(1): Total Repetitions achieved in constant time, higher means more confident result
(2): Single Repetition Duration (ns/op), lower is better
(3): Heap Memory (B/op), lower is better
(4): Average Allocations per Repetition (allocs/op), lower is better
## Gin v1. stable
@@ -281,10 +281,10 @@ func main() {
// single file
file, _ := c.FormFile("file")
log.Println(file.Filename)
// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)
// c.SaveUploadedFile(file, dst)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
@@ -313,9 +313,9 @@ func main() {
for _, file := range files {
log.Println(file.Filename)
// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)
// c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
@@ -369,6 +369,7 @@ r := gin.New()
instead of
```go
// Default With the Logger and Recovery middleware already attached
r := gin.Default()
```
@@ -380,7 +381,11 @@ func main() {
r := gin.New()
// Global middleware
// Logger middleware will write the logs to gin.DefaultWriter even you set with GIN_MODE=release.
// By default gin.DefaultWriter = os.Stdout
r.Use(gin.Logger())
// Recovery middleware recovers from any panics and writes a 500 if there was one.
r.Use(gin.Recovery())
// Per route middleware, you can add as many as you desire.
@@ -408,6 +413,28 @@ func main() {
}
```
### How to write log file
```go
func main() {
// Disable Console Color, you don't need console color when writing the logs to file.
gin.DisableConsoleColor()
// Logging to a file.
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// Use the following code if you need to write the logs to file and console at the same time.
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
r.Run(":8080")
}
```
### Model binding and validation
To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).
@@ -460,6 +487,67 @@ func main() {
}
```
### Custom Validators
It is also possible to register custom validators. See the [example code](examples/custom-validation/server.go).
[embedmd]:# (examples/custom-validation/server.go go)
```go
package main
import (
"net/http"
"reflect"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
validator "gopkg.in/go-playground/validator.v8"
)
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
}
func bookableDate(
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
if date, ok := field.Interface().(time.Time); ok {
today := time.Now()
if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
return false
}
}
return true
}
func main() {
route := gin.Default()
binding.Validator.RegisterValidation("bookabledate", bookableDate)
route.GET("/bookable", getBookable)
route.Run(":8085")
}
func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}
```
```console
$ curl "localhost:8085/bookable?check_in=2017-08-16&check_out=2017-08-17"
{"message":"Booking dates are valid!"}
$ curl "localhost:8085/bookable?check_in=2017-08-15&check_out=2017-08-16"
{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}
```
### Only Bind Query String
`BindQuery` function only binds the query params and not the post data. See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017).
@@ -505,10 +593,12 @@ package main
import "log"
import "github.com/gin-gonic/gin"
import "time"
type Person struct {
Name string `form:"name"`
Address string `form:"address"`
Name string `form:"name"`
Address string `form:"address"`
Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
}
func main() {
@@ -525,12 +615,18 @@ func startPage(c *gin.Context) {
if c.Bind(&person) == nil {
log.Println(person.Name)
log.Println(person.Address)
log.Println(person.Birthday)
}
c.String(200, "Success")
}
```
Test it with:
```sh
$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15"
```
### Bind HTML checkboxes
See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
@@ -676,7 +772,7 @@ func main() {
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
```
```
### Serving static files
@@ -787,7 +883,7 @@ You may use custom delims
r := gin.Default()
r.Delims("{[{", "}]}")
r.LoadHTMLGlob("/path/to/templates"))
```
```
#### Custom Template Funcs

View File

@@ -47,7 +47,7 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
pairs := processAccounts(accounts)
return func(c *Context) {
// Search user in the slice of allowed credentials
user, found := pairs.searchCredential(c.Request.Header.Get("Authorization"))
user, found := pairs.searchCredential(c.requestHeader("Authorization"))
if !found {
// Credentials doesn't match, we return 401 and abort handlers chain.
c.Header("WWW-Authenticate", realm)

View File

@@ -4,7 +4,11 @@
package binding
import "net/http"
import (
"net/http"
validator "gopkg.in/go-playground/validator.v8"
)
const (
MIMEJSON = "application/json"
@@ -31,6 +35,11 @@ type StructValidator interface {
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
// Otherwise nil must be returned.
ValidateStruct(interface{}) error
// RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key
// NOTE: if the key already exists, the previous validation function will be replaced.
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
RegisterValidation(string, validator.Func) error
}
var Validator StructValidator = &defaultValidator{}

View File

@@ -28,6 +28,11 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error {
return nil
}
func (v *defaultValidator) RegisterValidation(key string, fn validator.Func) error {
v.lazyinit()
return v.validate.RegisterValidation(key, fn)
}
func (v *defaultValidator) lazyinit() {
v.once.Do(func() {
config := &validator.Config{TagName: "binding"}

View File

@@ -6,6 +6,8 @@ package binding
import "net/http"
const defaultMemory = 32 * 1024 * 1024
type formBinding struct{}
type formPostBinding struct{}
type formMultipartBinding struct{}
@@ -18,7 +20,7 @@ func (formBinding) Bind(req *http.Request, obj interface{}) error {
if err := req.ParseForm(); err != nil {
return err
}
req.ParseMultipartForm(32 << 10) // 32 MB
req.ParseMultipartForm(defaultMemory)
if err := mapForm(obj, req.Form); err != nil {
return err
}
@@ -44,7 +46,7 @@ func (formMultipartBinding) Name() string {
}
func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
if err := req.ParseMultipartForm(32 << 10); err != nil {
if err := req.ParseMultipartForm(defaultMemory); err != nil {
return err
}
if err := mapForm(obj, req.MultipartForm.Value); err != nil {

View File

@@ -6,9 +6,12 @@ package binding
import (
"bytes"
"reflect"
"testing"
"time"
validator "gopkg.in/go-playground/validator.v8"
"github.com/stretchr/testify/assert"
)
@@ -190,3 +193,42 @@ func TestValidatePrimitives(t *testing.T) {
assert.NoError(t, validate(&str))
assert.Equal(t, str, "value")
}
// structCustomValidation is a helper struct we use to check that
// custom validation can be registered on it.
// The `notone` binding directive is for custom validation and registered later.
type structCustomValidation struct {
Integer int `binding:"notone"`
}
// notOne is a custom validator meant to be used with `validator.v8` library.
// The method signature for `v9` is significantly different and this function
// would need to be changed for tests to pass after upgrade.
// See https://github.com/gin-gonic/gin/pull/1015.
func notOne(
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
if val, ok := field.Interface().(int); ok {
return val != 1
}
return false
}
func TestRegisterValidation(t *testing.T) {
// This validates that the function `notOne` matches
// the expected function signature by `defaultValidator`
// and by extension the validator library.
err := Validator.RegisterValidation("notone", notOne)
// Check that we can register custom validation without error
assert.Nil(t, err)
// Create an instance which will fail validation
withOne := structCustomValidation{Integer: 1}
errs := validate(withOne)
// Check that we got back non-nil errs
assert.NotNil(t, errs)
// Check that the error matches expactation
assert.Error(t, errs, "", "", "notone")
}

View File

@@ -509,7 +509,7 @@ func (c *Context) ClientIP() string {
}
if c.engine.AppEngine {
if addr := c.Request.Header.Get("X-Appengine-Remote-Addr"); addr != "" {
if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" {
return addr
}
}
@@ -537,10 +537,7 @@ func (c *Context) IsWebsocket() bool {
}
func (c *Context) requestHeader(key string) string {
if values, _ := c.Request.Header[key]; len(values) > 0 {
return values[0]
}
return ""
return c.Request.Header.Get(key)
}
/************************************/
@@ -586,6 +583,9 @@ func (c *Context) GetRawData() ([]byte, error) {
return ioutil.ReadAll(c.Request.Body)
}
// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
if path == "" {
path = "/"
@@ -601,6 +601,10 @@ func (c *Context) SetCookie(name, value string, maxAge int, path, domain string,
})
}
// Cookie returns the named cookie provided in the request or
// ErrNoCookie if not found. And return the named cookie is unescaped.
// If multiple cookies match the given name, only one cookie will
// be returned.
func (c *Context) Cookie(name string) (string, error) {
cookie, err := c.Request.Cookie(name)
if err != nil {

View File

@@ -0,0 +1,45 @@
package main
import (
"net/http"
"reflect"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
validator "gopkg.in/go-playground/validator.v8"
)
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
}
func bookableDate(
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
if date, ok := field.Interface().(time.Time); ok {
today := time.Now()
if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
return false
}
}
return true
}
func main() {
route := gin.Default()
binding.Validator.RegisterValidation("bookabledate", bookableDate)
route.GET("/bookable", getBookable)
route.Run(":8085")
}
func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}

View File

@@ -316,14 +316,11 @@ func (engine *Engine) HandleContext(c *Context) {
func (engine *Engine) handleHTTPRequest(context *Context) {
httpMethod := context.Request.Method
var path string
var unescape bool
path := context.Request.URL.Path
unescape := false
if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
path = context.Request.URL.RawPath
unescape = engine.UnescapePathValues
} else {
path = context.Request.URL.Path
unescape = false
}
// Find root of the tree for the given HTTP method

View File

@@ -9,15 +9,15 @@ import (
"net/http"
"sync"
. "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
)
var once sync.Once
var internalEngine *Engine
var internalEngine *gin.Engine
func engine() *Engine {
func engine() *gin.Engine {
once.Do(func() {
internalEngine = Default()
internalEngine = gin.Default()
})
return internalEngine
}
@@ -35,65 +35,65 @@ func SetHTMLTemplate(templ *template.Template) {
}
// NoRoute adds handlers for NoRoute. It return a 404 code by default.
func NoRoute(handlers ...HandlerFunc) {
func NoRoute(handlers ...gin.HandlerFunc) {
engine().NoRoute(handlers...)
}
// NoMethod sets the handlers called when... TODO
func NoMethod(handlers ...HandlerFunc) {
func NoMethod(handlers ...gin.HandlerFunc) {
engine().NoMethod(handlers...)
}
// Group creates a new router group. You should add all the routes that have common middlwares or the same path prefix.
// For example, all the routes that use a common middlware for authorization could be grouped.
func Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
func Group(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup {
return engine().Group(relativePath, handlers...)
}
func Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
func Handle(httpMethod, relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().Handle(httpMethod, relativePath, handlers...)
}
// POST is a shortcut for router.Handle("POST", path, handle)
func POST(relativePath string, handlers ...HandlerFunc) IRoutes {
func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().POST(relativePath, handlers...)
}
// GET is a shortcut for router.Handle("GET", path, handle)
func GET(relativePath string, handlers ...HandlerFunc) IRoutes {
func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().GET(relativePath, handlers...)
}
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
func DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
func DELETE(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().DELETE(relativePath, handlers...)
}
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
func PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
func PATCH(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().PATCH(relativePath, handlers...)
}
// PUT is a shortcut for router.Handle("PUT", path, handle)
func PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
func PUT(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().PUT(relativePath, handlers...)
}
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
func OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
func OPTIONS(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().OPTIONS(relativePath, handlers...)
}
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
func HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
func HEAD(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().HEAD(relativePath, handlers...)
}
func Any(relativePath string, handlers ...HandlerFunc) IRoutes {
func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().Any(relativePath, handlers...)
}
func StaticFile(relativePath, filepath string) IRoutes {
func StaticFile(relativePath, filepath string) gin.IRoutes {
return engine().StaticFile(relativePath, filepath)
}
@@ -103,18 +103,18 @@ func StaticFile(relativePath, filepath string) IRoutes {
// To use the operating system's file system implementation,
// use :
// router.Static("/static", "/var/www")
func Static(relativePath, root string) IRoutes {
func Static(relativePath, root string) gin.IRoutes {
return engine().Static(relativePath, root)
}
func StaticFS(relativePath string, fs http.FileSystem) IRoutes {
func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes {
return engine().StaticFS(relativePath, fs)
}
// Use attachs a global middleware to the router. ie. the middlewares attached though Use() will be
// included in the handlers chain for every single request. Even 404, 405, static files...
// For example, this is the right place for a logger or error management middleware.
func Use(middlewares ...HandlerFunc) IRoutes {
func Use(middlewares ...gin.HandlerFunc) gin.IRoutes {
return engine().Use(middlewares...)
}

View File

@@ -91,10 +91,11 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
clientIP := c.ClientIP()
method := c.Request.Method
statusCode := c.Writer.Status()
var statusColor, methodColor string
var statusColor, methodColor, resetColor string
if isTerm {
statusColor = colorForStatus(statusCode)
methodColor = colorForMethod(method)
resetColor = reset
}
comment := c.Errors.ByType(ErrorTypePrivate).String()
@@ -104,10 +105,10 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s",
end.Format("2006/01/02 - 15:04:05"),
statusColor, statusCode, reset,
statusColor, statusCode, resetColor,
latency,
clientIP,
methodColor, method, reset,
methodColor, method, resetColor,
path,
comment,
)

View File

@@ -26,6 +26,7 @@ func Recovery() HandlerFunc {
return RecoveryWithWriter(DefaultErrorWriter)
}
// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.
func RecoveryWithWriter(out io.Writer) HandlerFunc {
var logger *log.Logger
if out != nil {
@@ -46,7 +47,7 @@ func RecoveryWithWriter(out io.Writer) HandlerFunc {
}
}
// stack returns a nicely formated stack frame, skipping skip frames.
// stack returns a nicely formatted stack frame, skipping skip frames.
func stack(skip int) []byte {
buf := new(bytes.Buffer) // the returned data
// As we loop, we open files and read them. These variables record the currently