update gin to v1.2

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
Bo-Yi Wu 2017-08-04 10:43:13 +08:00
parent 6864ffe9f0
commit b9104de2c5
21 changed files with 237 additions and 414 deletions

View File

@ -1,46 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at teamgingonic@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -1,13 +0,0 @@
## Contributing
- With issues:
- Use the search tool before opening a new issue.
- Please provide source code and commit sha if you found a bug.
- Review existing issues and provide feedback or react to them.
- With pull requests:
- Open your pull request against `master`
- Your pull request should have no more than two commits, if not you should squash them.
- It should pass all tests in the available continuous integrations systems such as TravisCI.
- You should add/modify tests to cover your proposed code changes.
- If your pull request contains a new feature, please document it on the README.

View File

@ -1,6 +1,6 @@
# Gin Web Framework # Gin Web Framework
<img align="right" width="159px" src="https://raw.githubusercontent.com/gin-gonic/logo/master/color.png"> <img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.jpg">
[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) [![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) [![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin)
@ -13,8 +13,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
![Gin console logger](https://gin-gonic.github.io/gin/other/console.png) ![Gin console logger](https://gin-gonic.github.io/gin/other/console.png)
```sh ```sh
# assume the following codes in example.go file $ cat test.go
$ cat example.go
``` ```
```go ```go
@ -33,11 +32,6 @@ func main() {
} }
``` ```
```
# run example.go and visit 0.0.0.0:8080/ping on browser
$ go run example.go
```
## Benchmarks ## 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)
@ -109,38 +103,6 @@ import "github.com/gin-gonic/gin"
import "net/http" import "net/http"
``` ```
### Use a vendor tool like [Govendor](https://github.com/kardianos/govendor)
1. `go get` govendor
```sh
$ go get github.com/kardianos/govendor
```
2. Create your project folder and `cd` inside
```sh
$ mkdir -p ~/go/src/github.com/myusername/project && cd "$_"
```
3. Vendor init your project and add gin
```sh
$ govendor init
$ govendor fetch github.com/gin-gonic/gin@v1.2
```
4. Copy a starting template inside your project
```sh
$ curl https://raw.githubusercontent.com/gin-gonic/gin/master/examples/basic/main.go > main.go
```
5. Run your project
```sh
$ go run main.go
```
## API Examples ## API Examples
### Using GET, POST, PUT, PATCH, DELETE and OPTIONS ### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
@ -276,7 +238,7 @@ func main() {
file, _ := c.FormFile("file") file, _ := c.FormFile("file")
log.Println(file.Filename) log.Println(file.Filename)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) c.String(http.StatusOK, fmt.Printf("'%s' uploaded!", file.Filename))
}) })
router.Run(":8080") router.Run(":8080")
} }
@ -305,7 +267,7 @@ func main() {
for _, file := range files { for _, file := range files {
log.Println(file.Filename) log.Println(file.Filename)
} }
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) c.String(http.StatusOK, fmt.Printf("%d files uploaded!", len(files)))
}) })
router.Run(":8080") router.Run(":8080")
} }
@ -471,7 +433,7 @@ func startPage(c *gin.Context) {
var person Person var person Person
// If `GET`, only `Form` binding engine (`query`) used. // If `GET`, only `Form` binding engine (`query`) used.
// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`). // If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
// See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48 // See more at https://github.com/gin-gonic/gin/blob/develop/binding/binding.go#L45
if c.Bind(&person) == nil { if c.Bind(&person) == nil {
log.Println(person.Name) log.Println(person.Name)
log.Println(person.Address) log.Println(person.Address)
@ -481,52 +443,6 @@ func startPage(c *gin.Context) {
} }
``` ```
### Bind HTML checkboxes
See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
main.go
```go
...
type myForm struct {
Colors []string `form:"colors[]"`
}
...
func formHandler(c *gin.Context) {
var fakeForm myForm
c.Bind(&fakeForm)
c.JSON(200, gin.H{"color": fakeForm.Colors})
}
...
```
form.html
```html
<form action="/" method="POST">
<p>Check some colors</p>
<label for="red">Red</label>
<input type="checkbox" name="colors[]" value="red" id="red" />
<label for="green">Green</label>
<input type="checkbox" name="colors[]" value="green" id="green" />
<label for="blue">Blue</label>
<input type="checkbox" name="colors[]" value="blue" id="blue" />
<input type="submit" />
</form>
```
result:
```
{"color":["red","green","blue"]}
```
### Multipart/Urlencoded binding ### Multipart/Urlencoded binding
```go ```go
@ -605,29 +521,6 @@ func main() {
} }
``` ```
#### SecureJSON
Using SecureJSON to prevent json hijacking. Default prepends `"while(1),"` to response body if the given struct is array values.
```go
func main() {
r := gin.Default()
// You can also use your own secure json prefix
// r.SecureJsonPrefix(")]}',\n")
r.GET("/someJSON", func(c *gin.Context) {
names := []string{"lena", "austin", "foo"}
// Will output : while(1);["lena","austin","foo"]
c.SecureJSON(http.StatusOK, names)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
```
### Serving static files ### Serving static files
```go ```go
@ -714,8 +607,6 @@ templates/users/index.tmpl
{{ end }} {{ end }}
``` ```
#### Custom Template renderer
You can also use your own html template render You can also use your own html template render
```go ```go
@ -729,8 +620,6 @@ func main() {
} }
``` ```
#### Custom Delimiters
You may use custom delims You may use custom delims
```go ```go
@ -739,7 +628,7 @@ You may use custom delims
r.LoadHTMLGlob("/path/to/templates")) r.LoadHTMLGlob("/path/to/templates"))
``` ```
#### Custom Template Funcs #### Add custom template funcs
main.go main.go
@ -989,7 +878,7 @@ func main() {
Cache: autocert.DirCache("/var/www/.cache"), Cache: autocert.DirCache("/var/www/.cache"),
} }
log.Fatal(autotls.RunWithManager(r, &m)) log.Fatal(autotls.RunWithManager(r, m))
} }
``` ```
@ -1067,7 +956,20 @@ func main() {
} }
``` ```
## Users [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge) ## Contributing
- With issues:
- Use the search tool before opening a new issue.
- Please provide source code and commit sha if you found a bug.
- Review existing issues and provide feedback or react to them.
- With pull requests:
- Open your pull request against develop
- Your pull request should have no more than two commits, if not you should squash them.
- It should pass all tests in the available continuous integrations systems such as TravisCI.
- You should add/modify tests to cover your proposed code changes.
- If your pull request contains a new feature, please document it on the README.
## Users
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.

View File

@ -10,18 +10,16 @@ import (
"strconv" "strconv"
) )
// AuthUserKey is the cookie name for user credential in basic auth
const AuthUserKey = "user" const AuthUserKey = "user"
// Accounts defines a key/value for user/pass list of authorized logins type (
type Accounts map[string]string Accounts map[string]string
authPair struct {
type authPair struct { Value string
Value string User string
User string }
} authPairs []authPair
)
type authPairs []authPair
func (a authPairs) searchCredential(authValue string) (string, bool) { func (a authPairs) searchCredential(authValue string) (string, bool) {
if len(authValue) == 0 { if len(authValue) == 0 {
@ -89,6 +87,6 @@ func secureCompare(given, actual string) bool {
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 { if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1 return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
} }
// Securely compare actual to itself to keep constant time, but always return false /* Securely compare actual to itself to keep constant time, but always return false */
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
} }

View File

@ -48,15 +48,9 @@ type Context struct {
handlers HandlersChain handlers HandlersChain
index int8 index int8
engine *Engine engine *Engine
Keys map[string]interface{}
// Keys is a key/value pair exclusively for the context of each request Errors errorMsgs
Keys map[string]interface{}
// Errors is a list of errors attached to all the handlers/middlewares who used this context
Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation
Accepted []string Accepted []string
} }
@ -85,8 +79,8 @@ func (c *Context) Copy() *Context {
return &cp return &cp
} }
// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", // HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", this
// this function will return "main.handleGetUsers" // function will return "main.handleGetUsers"
func (c *Context) HandlerName() string { func (c *Context) HandlerName() string {
return nameOfFunction(c.handlers.Last()) return nameOfFunction(c.handlers.Last())
} }
@ -117,8 +111,8 @@ func (c *Context) IsAborted() bool {
} }
// Abort prevents pending handlers from being called. Note that this will not stop the current handler. // Abort prevents pending handlers from being called. Note that this will not stop the current handler.
// Let's say you have an authorization middleware that validates that the current request is authorized. // Let's say you have an authorization middleware that validates that the current request is authorized. If the
// If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers // authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
// for this request are not called. // for this request are not called.
func (c *Context) Abort() { func (c *Context) Abort() {
c.index = abortIndex c.index = abortIndex
@ -132,16 +126,15 @@ func (c *Context) AbortWithStatus(code int) {
c.Abort() c.Abort()
} }
// AbortWithStatusJSON calls `Abort()` and then `JSON` internally. // AbortWithStatusJSON calls `Abort()` and then `JSON` internally. This method stops the chain, writes the status code and return a JSON body
// This method stops the chain, writes the status code and return a JSON body.
// It also sets the Content-Type as "application/json". // It also sets the Content-Type as "application/json".
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) { func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) {
c.Abort() c.Abort()
c.JSON(code, jsonObj) c.JSON(code, jsonObj)
} }
// AbortWithError calls `AbortWithStatus()` and `Error()` internally. // AbortWithError calls `AbortWithStatus()` and `Error()` internally. This method stops the chain, writes the status code and
// This method stops the chain, writes the status code and pushes the specified error to `c.Errors`. // pushes the specified error to `c.Errors`.
// See Context.Error() for more details. // See Context.Error() for more details.
func (c *Context) AbortWithError(code int, err error) *Error { func (c *Context) AbortWithError(code int, err error) *Error {
c.AbortWithStatus(code) c.AbortWithStatus(code)
@ -152,15 +145,11 @@ func (c *Context) AbortWithError(code int, err error) *Error {
/********* ERROR MANAGEMENT *********/ /********* ERROR MANAGEMENT *********/
/************************************/ /************************************/
// Error attaches an error to the current context. The error is pushed to a list of errors. // Attaches an error to the current context. The error is pushed to a list of errors.
// It's a good idea to call Error for each error that occurred during the resolution of a request. // It's a good idea to call Error for each error that occurred during the resolution of a request.
// A middleware can be used to collect all the errors and push them to a database together, // A middleware can be used to collect all the errors
// print a log, or append it in the HTTP response. // and push them to a database together, print a log, or append it in the HTTP response.
// Error will panic if err is nil.
func (c *Context) Error(err error) *Error { func (c *Context) Error(err error) *Error {
if err == nil {
panic("err is nil")
}
var parsedError *Error var parsedError *Error
switch err.(type) { switch err.(type) {
case *Error: case *Error:
@ -297,10 +286,10 @@ func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)
// Param returns the value of the URL param. // Param returns the value of the URL param.
// It is a shortcut for c.Params.ByName(key) // It is a shortcut for c.Params.ByName(key)
// router.GET("/user/:id", func(c *gin.Context) { // router.GET("/user/:id", func(c *gin.Context) {
// // a GET request to /user/john // // a GET request to /user/john
// id := c.Param("id") // id == "john" // id := c.Param("id") // id == "john"
// }) // })
func (c *Context) Param(key string) string { func (c *Context) Param(key string) string {
return c.Params.ByName(key) return c.Params.ByName(key)
} }
@ -308,11 +297,11 @@ func (c *Context) Param(key string) string {
// Query returns the keyed url query value if it exists, // Query returns the keyed url query value if it exists,
// otherwise it returns an empty string `("")`. // otherwise it returns an empty string `("")`.
// It is shortcut for `c.Request.URL.Query().Get(key)` // It is shortcut for `c.Request.URL.Query().Get(key)`
// GET /path?id=1234&name=Manu&value= // GET /path?id=1234&name=Manu&value=
// c.Query("id") == "1234" // c.Query("id") == "1234"
// c.Query("name") == "Manu" // c.Query("name") == "Manu"
// c.Query("value") == "" // c.Query("value") == ""
// c.Query("wtf") == "" // c.Query("wtf") == ""
func (c *Context) Query(key string) string { func (c *Context) Query(key string) string {
value, _ := c.GetQuery(key) value, _ := c.GetQuery(key)
return value return value
@ -321,10 +310,10 @@ func (c *Context) Query(key string) string {
// DefaultQuery returns the keyed url query value if it exists, // DefaultQuery returns the keyed url query value if it exists,
// otherwise it returns the specified defaultValue string. // otherwise it returns the specified defaultValue string.
// See: Query() and GetQuery() for further information. // See: Query() and GetQuery() for further information.
// GET /?name=Manu&lastname= // GET /?name=Manu&lastname=
// c.DefaultQuery("name", "unknown") == "Manu" // c.DefaultQuery("name", "unknown") == "Manu"
// c.DefaultQuery("id", "none") == "none" // c.DefaultQuery("id", "none") == "none"
// c.DefaultQuery("lastname", "none") == "" // c.DefaultQuery("lastname", "none") == ""
func (c *Context) DefaultQuery(key, defaultValue string) string { func (c *Context) DefaultQuery(key, defaultValue string) string {
if value, ok := c.GetQuery(key); ok { if value, ok := c.GetQuery(key); ok {
return value return value
@ -336,10 +325,10 @@ func (c *Context) DefaultQuery(key, defaultValue string) string {
// if it exists `(value, true)` (even when the value is an empty string), // if it exists `(value, true)` (even when the value is an empty string),
// otherwise it returns `("", false)`. // otherwise it returns `("", false)`.
// It is shortcut for `c.Request.URL.Query().Get(key)` // It is shortcut for `c.Request.URL.Query().Get(key)`
// GET /?name=Manu&lastname= // GET /?name=Manu&lastname=
// ("Manu", true) == c.GetQuery("name") // ("Manu", true) == c.GetQuery("name")
// ("", false) == c.GetQuery("id") // ("", false) == c.GetQuery("id")
// ("", true) == c.GetQuery("lastname") // ("", true) == c.GetQuery("lastname")
func (c *Context) GetQuery(key string) (string, bool) { func (c *Context) GetQuery(key string) (string, bool) {
if values, ok := c.GetQueryArray(key); ok { if values, ok := c.GetQueryArray(key); ok {
return values[0], ok return values[0], ok
@ -385,9 +374,9 @@ func (c *Context) DefaultPostForm(key, defaultValue string) string {
// form or multipart form when it exists `(value, true)` (even when the value is an empty string), // form or multipart form when it exists `(value, true)` (even when the value is an empty string),
// otherwise it returns ("", false). // otherwise it returns ("", false).
// For example, during a PATCH request to update the user's email: // For example, during a PATCH request to update the user's email:
// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com" // email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
// email= --> ("", true) := GetPostForm("email") // set email to "" // email= --> ("", true) := GetPostForm("email") // set email to ""
// --> ("", false) := GetPostForm("email") // do nothing with email // --> ("", false) := GetPostForm("email") // do nothing with email
func (c *Context) GetPostForm(key string) (string, bool) { func (c *Context) GetPostForm(key string) (string, bool) {
if values, ok := c.GetPostFormArray(key); ok { if values, ok := c.GetPostFormArray(key); ok {
return values[0], ok return values[0], ok
@ -433,8 +422,8 @@ func (c *Context) MultipartForm() (*multipart.Form, error) {
// Bind checks the Content-Type to select a binding engine automatically, // Bind checks the Content-Type to select a binding engine automatically,
// Depending the "Content-Type" header different bindings are used: // Depending the "Content-Type" header different bindings are used:
// "application/json" --> JSON binding // "application/json" --> JSON binding
// "application/xml" --> XML binding // "application/xml" --> XML binding
// otherwise --> returns an error // otherwise --> returns an error
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
// It decodes the json payload into the struct specified as a pointer. // It decodes the json payload into the struct specified as a pointer.
@ -563,7 +552,15 @@ func (c *Context) GetRawData() ([]byte, error) {
return ioutil.ReadAll(c.Request.Body) return ioutil.ReadAll(c.Request.Body)
} }
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) { func (c *Context) SetCookie(
name string,
value string,
maxAge int,
path string,
domain string,
secure bool,
httpOnly bool,
) {
if path == "" { if path == "" {
path = "/" path = "/"
} }
@ -617,13 +614,6 @@ func (c *Context) IndentedJSON(code int, obj interface{}) {
c.Render(code, render.IndentedJSON{Data: obj}) c.Render(code, render.IndentedJSON{Data: obj})
} }
// SecureJSON serializes the given struct as Secure JSON into the response body.
// Default prepends "while(1)," to response body if the given struct is array values.
// It also sets the Content-Type as "application/json".
func (c *Context) SecureJSON(code int, obj interface{}) {
c.Render(code, render.SecureJSON{Prefix: c.engine.secureJsonPrefix, Data: obj})
}
// JSON serializes the given struct as JSON into the response body. // JSON serializes the given struct as JSON into the response body.
// It also sets the Content-Type as "application/json". // It also sets the Content-Type as "application/json".
func (c *Context) JSON(code int, obj interface{}) { func (c *Context) JSON(code int, obj interface{}) {

View File

@ -15,7 +15,7 @@ func init() {
} }
// IsDebugging returns true if the framework is running in debug mode. // IsDebugging returns true if the framework is running in debug mode.
// Use SetMode(gin.Release) to disable debug mode. // Use SetMode(gin.Release) to switch to disable the debug mode.
func IsDebugging() bool { func IsDebugging() bool {
return ginMode == debugCode return ginMode == debugCode
} }

View File

@ -5,11 +5,15 @@
package gin package gin
import ( import (
"log"
"github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/binding"
"log"
) )
func (c *Context) GetCookie(name string) (string, error) {
log.Println("GetCookie() method is deprecated. Use Cookie() instead.")
return c.Cookie(name)
}
// BindWith binds the passed struct pointer using the specified binding engine. // BindWith binds the passed struct pointer using the specified binding engine.
// See the binding package. // See the binding package.
func (c *Context) BindWith(obj interface{}, b binding.Binding) error { func (c *Context) BindWith(obj interface{}, b binding.Binding) error {

View File

@ -1,6 +0,0 @@
/*
Package gin implements a HTTP web framework called gin.
See https://gin-gonic.github.io/gin/ for more information about gin.
*/
package gin // import "github.com/gin-gonic/gin"

View File

@ -6,14 +6,11 @@ package gin
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"github.com/json-iterator/go"
) )
var json = jsoniter.ConfigCompatibleWithStandardLibrary
type ErrorType uint64 type ErrorType uint64
const ( const (
@ -26,13 +23,15 @@ const (
ErrorTypeNu = 2 ErrorTypeNu = 2
) )
type Error struct { type (
Err error Error struct {
Type ErrorType Err error
Meta interface{} Type ErrorType
} Meta interface{}
}
type errorMsgs []*Error errorMsgs []*Error
)
var _ error = &Error{} var _ error = &Error{}
@ -72,7 +71,7 @@ func (msg *Error) MarshalJSON() ([]byte, error) {
return json.Marshal(msg.JSON()) return json.Marshal(msg.JSON())
} }
// Error implements the error interface // Implements the error interface
func (msg Error) Error() string { func (msg Error) Error() string {
return msg.Err.Error() return msg.Err.Error()
} }
@ -81,7 +80,7 @@ func (msg *Error) IsType(flags ErrorType) bool {
return (msg.Type & flags) > 0 return (msg.Type & flags) > 0
} }
// ByType returns a readonly copy filtered the byte. // Returns a readonly copy filtered the byte.
// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic // ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic
func (a errorMsgs) ByType(typ ErrorType) errorMsgs { func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
if len(a) == 0 { if len(a) == 0 {
@ -99,16 +98,17 @@ func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
return result return result
} }
// Last returns the last error in the slice. It returns nil if the array is empty. // Returns the last error in the slice. It returns nil if the array is empty.
// Shortcut for errors[len(errors)-1] // Shortcut for errors[len(errors)-1]
func (a errorMsgs) Last() *Error { func (a errorMsgs) Last() *Error {
if length := len(a); length > 0 { length := len(a)
if length > 0 {
return a[length-1] return a[length-1]
} }
return nil return nil
} }
// Errors returns an array will all the error messages. // Returns an array will all the error messages.
// Example: // Example:
// c.Error(errors.New("first")) // c.Error(errors.New("first"))
// c.Error(errors.New("second")) // c.Error(errors.New("second"))

View File

@ -9,13 +9,14 @@ import (
"os" "os"
) )
type onlyfilesFS struct { type (
fs http.FileSystem onlyfilesFS struct {
} fs http.FileSystem
}
type neuteredReaddirFile struct { neuteredReaddirFile struct {
http.File http.File
} }
)
// Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally // Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally
// in router.Static(). // in router.Static().
@ -29,7 +30,7 @@ func Dir(root string, listDirectory bool) http.FileSystem {
return &onlyfilesFS{fs} return &onlyfilesFS{fs}
} }
// Open conforms to http.Filesystem // Conforms to http.Filesystem
func (fs onlyfilesFS) Open(name string) (http.File, error) { func (fs onlyfilesFS) Open(name string) (http.File, error) {
f, err := fs.fs.Open(name) f, err := fs.fs.Open(name)
if err != nil { if err != nil {
@ -38,7 +39,7 @@ func (fs onlyfilesFS) Open(name string) (http.File, error) {
return neuteredReaddirFile{f}, nil return neuteredReaddirFile{f}, nil
} }
// Readdir overrides the http.File default implementation // Overrides the http.File default implementation
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) { func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
// this disables directory listing // this disables directory listing
return nil, nil return nil, nil

View File

@ -26,73 +26,74 @@ 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 own.
func (c HandlersChain) Last() HandlerFunc { func (c HandlersChain) Last() HandlerFunc {
if length := len(c); length > 0 { length := len(c)
if length > 0 {
return c[length-1] return c[length-1]
} }
return nil return nil
} }
type RouteInfo struct { type (
Method string RoutesInfo []RouteInfo
Path string RouteInfo struct {
Handler string Method string
} Path string
Handler string
}
type RoutesInfo []RouteInfo // Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
// Create an instance of Engine, by using New() or Default()
Engine struct {
RouterGroup
delims render.Delims
HTMLRender render.HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync.Pool
trees methodTrees
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings. // Enables automatic redirection if the current route can't be matched but a
// Create an instance of Engine, by using New() or Default() // handler for the path with (without) the trailing slash exists.
type Engine struct { // For example if /foo/ is requested but a route only exists for /foo, the
RouterGroup // client is redirected to /foo with http status code 301 for GET requests
delims render.Delims // and 307 for all other request methods.
secureJsonPrefix string RedirectTrailingSlash bool
HTMLRender render.HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync.Pool
trees methodTrees
// Enables automatic redirection if the current route can't be matched but a // If enabled, the router tries to fix the current request path, if no
// handler for the path with (without) the trailing slash exists. // handle is registered for it.
// For example if /foo/ is requested but a route only exists for /foo, the // First superfluous path elements like ../ or // are removed.
// client is redirected to /foo with http status code 301 for GET requests // Afterwards the router does a case-insensitive lookup of the cleaned path.
// and 307 for all other request methods. // If a handle can be found for this route, the router makes a redirection
RedirectTrailingSlash bool // to the corrected path with status code 301 for GET requests and 307 for
// all other request methods.
// For example /FOO and /..//Foo could be redirected to /foo.
// RedirectTrailingSlash is independent of this option.
RedirectFixedPath bool
// If enabled, the router tries to fix the current request path, if no // If enabled, the router checks if another method is allowed for the
// handle is registered for it. // current route, if the current request can not be routed.
// First superfluous path elements like ../ or // are removed. // If this is the case, the request is answered with 'Method Not Allowed'
// Afterwards the router does a case-insensitive lookup of the cleaned path. // and HTTP status code 405.
// If a handle can be found for this route, the router makes a redirection // If no other Method is allowed, the request is delegated to the NotFound
// to the corrected path with status code 301 for GET requests and 307 for // handler.
// all other request methods. HandleMethodNotAllowed bool
// For example /FOO and /..//Foo could be redirected to /foo. ForwardedByClientIP bool
// RedirectTrailingSlash is independent of this option.
RedirectFixedPath bool
// If enabled, the router checks if another method is allowed for the // #726 #755 If enabled, it will thrust some headers starting with
// current route, if the current request can not be routed. // 'X-AppEngine...' for better integration with that PaaS.
// If this is the case, the request is answered with 'Method Not Allowed' AppEngine bool
// and HTTP status code 405.
// If no other Method is allowed, the request is delegated to the NotFound
// handler.
HandleMethodNotAllowed bool
ForwardedByClientIP bool
// #726 #755 If enabled, it will thrust some headers starting with // If enabled, the url.RawPath will be used to find parameters.
// 'X-AppEngine...' for better integration with that PaaS. UseRawPath bool
AppEngine bool // If true, the path value will be unescaped.
// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
// If enabled, the url.RawPath will be used to find parameters. // as url.Path gonna be used, which is already unescaped.
UseRawPath bool UnescapePathValues bool
// If true, the path value will be unescaped. }
// If UseRawPath is false (by default), the UnescapePathValues effectively is true, )
// as url.Path gonna be used, which is already unescaped.
UnescapePathValues bool
}
var _ IRouter = &Engine{} var _ IRouter = &Engine{}
@ -122,7 +123,6 @@ func New() *Engine {
UnescapePathValues: true, UnescapePathValues: true,
trees: make(methodTrees, 0, 9), trees: make(methodTrees, 0, 9),
delims: render.Delims{"{{", "}}"}, delims: render.Delims{"{{", "}}"},
secureJsonPrefix: "while(1);",
} }
engine.RouterGroup.engine = engine engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} { engine.pool.New = func() interface{} {
@ -147,11 +147,6 @@ func (engine *Engine) Delims(left, right string) *Engine {
return engine return engine
} }
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
engine.secureJsonPrefix = prefix
return engine
}
func (engine *Engine) LoadHTMLGlob(pattern string) { func (engine *Engine) LoadHTMLGlob(pattern string) {
if IsDebugging() { if IsDebugging() {
debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern))) debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)))
@ -291,7 +286,7 @@ func (engine *Engine) RunUnix(file string) (err error) {
return return
} }
// ServeHTTP conforms to the http.Handler interface. // Conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context) c := engine.pool.Get().(*Context)
c.writermem.reset(w) c.writermem.reset(w)
@ -303,7 +298,7 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
engine.pool.Put(c) engine.pool.Put(c)
} }
// HandleContext re-enter a context that has been rewritten. // Re-enter a context that has been rewritten.
// This can be done by setting c.Request.Path to your new target. // This can be done by setting c.Request.Path to your new target.
// Disclaimer: You can loop yourself to death with this, use wisely. // Disclaimer: You can loop yourself to death with this, use wisely.
func (engine *Engine) HandleContext(c *Context) { func (engine *Engine) HandleContext(c *Context) {
@ -351,6 +346,7 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
} }
} }
// TODO: unit test
if engine.HandleMethodNotAllowed { if engine.HandleMethodNotAllowed {
for _, tree := range engine.trees { for _, tree := range engine.trees {
if tree.method != httpMethod { if tree.method != httpMethod {

View File

@ -25,17 +25,14 @@ var (
disableColor = false disableColor = false
) )
// DisableConsoleColor disables color output in the console
func DisableConsoleColor() { func DisableConsoleColor() {
disableColor = true disableColor = true
} }
// ErrorLogger returns a handlerfunc for any error type
func ErrorLogger() HandlerFunc { func ErrorLogger() HandlerFunc {
return ErrorLoggerT(ErrorTypeAny) return ErrorLoggerT(ErrorTypeAny)
} }
// ErrorLoggerT returns a handlerfunc for a given error type
func ErrorLoggerT(typ ErrorType) HandlerFunc { func ErrorLoggerT(typ ErrorType) HandlerFunc {
return func(c *Context) { return func(c *Context) {
c.Next() c.Next()
@ -77,7 +74,6 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
// Start timer // Start timer
start := time.Now() start := time.Now()
path := c.Request.URL.Path path := c.Request.URL.Path
raw := c.Request.URL.RawQuery
// Process request // Process request
c.Next() c.Next()
@ -98,10 +94,6 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
} }
comment := c.Errors.ByType(ErrorTypePrivate).String() comment := c.Errors.ByType(ErrorTypePrivate).String()
if raw != "" {
path = path + "?" + raw
}
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s", fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s",
end.Format("2006/01/02 - 15:04:05"), end.Format("2006/01/02 - 15:04:05"),
statusColor, statusCode, reset, statusColor, statusCode, reset,

BIN
vendor/github.com/gin-gonic/gin/logo.jpg generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -64,10 +64,6 @@ func DisableBindValidation() {
binding.Validator = nil binding.Validator = nil
} }
func EnableJsonDecoderUseNumber() {
binding.EnableDecoderUseNumber = true
}
func Mode() string { func Mode() string {
return modeName return modeName
} }

View File

@ -5,7 +5,7 @@
package gin package gin
// cleanPath is the URL version of path.Clean, it returns a canonical URL path // CleanPath is the URL version of path.Clean, it returns a canonical URL path
// for p, eliminating . and .. elements. // for p, eliminating . and .. elements.
// //
// The following rules are applied iteratively until no further processing can // The following rules are applied iteratively until no further processing can

View File

@ -16,34 +16,36 @@ const (
defaultStatus = 200 defaultStatus = 200
) )
type ResponseWriter interface { type (
http.ResponseWriter ResponseWriter interface {
http.Hijacker http.ResponseWriter
http.Flusher http.Hijacker
http.CloseNotifier http.Flusher
http.CloseNotifier
// Returns the HTTP response status code of the current request. // Returns the HTTP response status code of the current request.
Status() int Status() int
// Returns the number of bytes already written into the response http body. // Returns the number of bytes already written into the response http body.
// See Written() // See Written()
Size() int Size() int
// Writes the string into the response body. // Writes the string into the response body.
WriteString(string) (int, error) WriteString(string) (int, error)
// Returns true if the response body was already written. // Returns true if the response body was already written.
Written() bool Written() bool
// Forces to write the http header (status code + headers). // Forces to write the http header (status code + headers).
WriteHeaderNow() WriteHeaderNow()
} }
type responseWriter struct { responseWriter struct {
http.ResponseWriter http.ResponseWriter
size int size int
status int status int
} }
)
var _ ResponseWriter = &responseWriter{} var _ ResponseWriter = &responseWriter{}
@ -95,7 +97,7 @@ func (w *responseWriter) Written() bool {
return w.size != noWritten return w.size != noWritten
} }
// Hijack implements the http.Hijacker interface // Implements the http.Hijacker interface
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if w.size < 0 { if w.size < 0 {
w.size = 0 w.size = 0
@ -103,12 +105,12 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.ResponseWriter.(http.Hijacker).Hijack() return w.ResponseWriter.(http.Hijacker).Hijack()
} }
// CloseNotify implements the http.CloseNotify interface // Implements the http.CloseNotify interface
func (w *responseWriter) CloseNotify() <-chan bool { func (w *responseWriter) CloseNotify() <-chan bool {
return w.ResponseWriter.(http.CloseNotifier).CloseNotify() return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
} }
// Flush implements the http.Flush interface // Implements the http.Flush interface
func (w *responseWriter) Flush() { func (w *responseWriter) Flush() {
w.ResponseWriter.(http.Flusher).Flush() w.ResponseWriter.(http.Flusher).Flush()
} }

View File

@ -11,37 +11,39 @@ import (
"strings" "strings"
) )
type IRouter interface { type (
IRoutes IRouter interface {
Group(string, ...HandlerFunc) *RouterGroup IRoutes
} Group(string, ...HandlerFunc) *RouterGroup
}
type IRoutes interface { IRoutes interface {
Use(...HandlerFunc) IRoutes Use(...HandlerFunc) IRoutes
Handle(string, string, ...HandlerFunc) IRoutes Handle(string, string, ...HandlerFunc) IRoutes
Any(string, ...HandlerFunc) IRoutes Any(string, ...HandlerFunc) IRoutes
GET(string, ...HandlerFunc) IRoutes GET(string, ...HandlerFunc) IRoutes
POST(string, ...HandlerFunc) IRoutes POST(string, ...HandlerFunc) IRoutes
DELETE(string, ...HandlerFunc) IRoutes DELETE(string, ...HandlerFunc) IRoutes
PATCH(string, ...HandlerFunc) IRoutes PATCH(string, ...HandlerFunc) IRoutes
PUT(string, ...HandlerFunc) IRoutes PUT(string, ...HandlerFunc) IRoutes
OPTIONS(string, ...HandlerFunc) IRoutes OPTIONS(string, ...HandlerFunc) IRoutes
HEAD(string, ...HandlerFunc) IRoutes HEAD(string, ...HandlerFunc) IRoutes
StaticFile(string, string) IRoutes StaticFile(string, string) IRoutes
Static(string, string) IRoutes Static(string, string) IRoutes
StaticFS(string, http.FileSystem) IRoutes StaticFS(string, http.FileSystem) IRoutes
} }
// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix // RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
// and an array of handlers (middleware) // and an array of handlers (middleware)
type RouterGroup struct { RouterGroup struct {
Handlers HandlersChain Handlers HandlersChain
basePath string basePath string
engine *Engine engine *Engine
root bool root bool
} }
)
var _ IRouter = &RouterGroup{} var _ IRouter = &RouterGroup{}

View File

@ -8,7 +8,6 @@ import (
"net/http" "net/http"
) )
// CreateTestContext returns a fresh engine and context for testing purposes
func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine) { func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine) {
r = New() r = New()
c = r.allocateContext() c = r.allocateContext()

View File

@ -105,7 +105,9 @@ func (n *node) incrementChildPrio(pos int) int {
newPos := pos newPos := pos
for newPos > 0 && n.children[newPos-1].priority < prio { for newPos > 0 && n.children[newPos-1].priority < prio {
// swap node positions // swap node positions
n.children[newPos-1], n.children[newPos] = n.children[newPos], n.children[newPos-1] tmpN := n.children[newPos-1]
n.children[newPos-1] = n.children[newPos]
n.children[newPos] = tmpN
newPos-- newPos--
} }
@ -357,7 +359,7 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle
n.handlers = handlers n.handlers = handlers
} }
// 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. // wildcards are saved to a map.
// If no handle can be found, a TSR (trailing slash redirect) recommendation is // 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 // made if a handle exists with an extra (without the) trailing slash for the
@ -499,7 +501,7 @@ 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 can optionally also fix trailing slashes.
// It returns the case-corrected path and a bool indicating whether the lookup // It returns the case-corrected path and a bool indicating whether the lookup
// was successful. // was successful.

View File

@ -100,10 +100,12 @@ func parseAccept(acceptHeader string) []string {
parts := strings.Split(acceptHeader, ",") parts := strings.Split(acceptHeader, ",")
out := make([]string, 0, len(parts)) out := make([]string, 0, len(parts))
for _, part := range parts { for _, part := range parts {
if index := strings.IndexByte(part, ';'); index >= 0 { index := strings.IndexByte(part, ';')
if index >= 0 {
part = part[0:index] part = part[0:index]
} }
if part = strings.TrimSpace(part); len(part) > 0 { part = strings.TrimSpace(part)
if len(part) > 0 {
out = append(out, part) out = append(out, part)
} }
} }

8
vendor/vendor.json vendored
View File

@ -129,10 +129,12 @@
"revisionTime": "2017-01-09T09:34:21Z" "revisionTime": "2017-01-09T09:34:21Z"
}, },
{ {
"checksumSHA1": "YYaHHK72ywsjJG11nEnz6JdGqSg=", "checksumSHA1": "cpELhz30Sco7IBwvI6FyuShnjnQ=",
"path": "github.com/gin-gonic/gin", "path": "github.com/gin-gonic/gin",
"revision": "5cb25a6410a299dcc31e7f31b41c60c90e30b3f6", "revision": "d459835d2b077e44f7c9b453505ee29881d5d12d",
"revisionTime": "2017-07-14T17:28:16Z" "revisionTime": "2017-07-02T09:28:26Z",
"version": "v1.2",
"versionExact": "v1.2"
}, },
{ {
"checksumSHA1": "A09l4+1MIuaXhh3Nn6aoOm+rr3Q=", "checksumSHA1": "A09l4+1MIuaXhh3Nn6aoOm+rr3Q=",