Upgrade to Go1.6, use vendor
Also make sure we're using a more recent version of godep
This commit is contained in:
2
vendor/github.com/gin-gonic/gin/.gitignore
generated
vendored
Normal file
2
vendor/github.com/gin-gonic/gin/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
Godeps/*
|
||||
!Godeps/Godeps.json
|
||||
6
vendor/github.com/gin-gonic/gin/.travis.yml
generated
vendored
Normal file
6
vendor/github.com/gin-gonic/gin/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
- tip
|
||||
174
vendor/github.com/gin-gonic/gin/AUTHORS.md
generated
vendored
Normal file
174
vendor/github.com/gin-gonic/gin/AUTHORS.md
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
List of all the awesome people working to make Gin the best Web Framework in Go.
|
||||
|
||||
|
||||
|
||||
##gin 0.x series authors
|
||||
|
||||
**Original Developer:** Manu Martinez-Almeida (@manucorporat)
|
||||
**Long-term Maintainer:** Javier Provecho (@javierprovecho)
|
||||
|
||||
People and companies, who have contributed, in alphabetical order.
|
||||
|
||||
**@achedeuzot (Klemen Sever)**
|
||||
- Fix newline debug printing
|
||||
|
||||
|
||||
**@adammck (Adam Mckaig)**
|
||||
- Add MIT license
|
||||
|
||||
|
||||
**@AlexanderChen1989 (Alexander)**
|
||||
- Typos in README
|
||||
|
||||
|
||||
**@alexandernyquist (Alexander Nyquist)**
|
||||
- Using template.Must to fix multiple return issue
|
||||
- ★ Added support for OPTIONS verb
|
||||
- ★ Setting response headers before calling WriteHeader
|
||||
- Improved documentation for model binding
|
||||
- ★ Added Content.Redirect()
|
||||
- ★ Added tons of Unit tests
|
||||
|
||||
|
||||
**@austinheap (Austin Heap)**
|
||||
- Added travis CI integration
|
||||
|
||||
|
||||
**@andredublin (Andre Dublin)**
|
||||
- Fix typo in comment
|
||||
|
||||
|
||||
**@bredov (Ludwig Valda Vasquez)**
|
||||
- Fix html templating in debug mode
|
||||
|
||||
|
||||
**@bluele (Jun Kimura)**
|
||||
- Fixes code examples in README
|
||||
|
||||
|
||||
**@chad-russell**
|
||||
- ★ Support for serializing gin.H into XML
|
||||
|
||||
|
||||
**@dickeyxxx (Jeff Dickey)**
|
||||
- Typos in README
|
||||
- Add example about serving static files
|
||||
|
||||
|
||||
**@dutchcoders (DutchCoders)**
|
||||
- ★ Fix security bug that allows client to spoof ip
|
||||
- Fix typo. r.HTMLTemplates -> SetHTMLTemplate
|
||||
|
||||
|
||||
**@fmd (Fareed Dudhia)**
|
||||
- Fix typo. SetHTTPTemplate -> SetHTMLTemplate
|
||||
|
||||
|
||||
**@jammie-stackhouse (Jamie Stackhouse)**
|
||||
- Add more shortcuts for router methods
|
||||
|
||||
|
||||
**@jasonrhansen**
|
||||
- Fix spelling and grammar errors in documentation
|
||||
|
||||
|
||||
**@JasonSoft (Jason Lee)**
|
||||
- Fix typo in comment
|
||||
|
||||
|
||||
**@joiggama (Ignacio Galindo)**
|
||||
- Add utf-8 charset header on renders
|
||||
|
||||
|
||||
**@julienschmidt (Julien Schmidt)**
|
||||
- gofmt the code examples
|
||||
|
||||
|
||||
**@kelcecil (Kel Cecil)**
|
||||
- Fix readme typo
|
||||
|
||||
|
||||
**@kyledinh (Kyle Dinh)**
|
||||
- Adds RunTLS()
|
||||
|
||||
|
||||
**@LinusU (Linus Unnebäck)**
|
||||
- Small fixes in README
|
||||
|
||||
|
||||
**@loongmxbt (Saint Asky)**
|
||||
- Fix typo in example
|
||||
|
||||
|
||||
**@lucas-clemente (Lucas Clemente)**
|
||||
- ★ work around path.Join removing trailing slashes from routes
|
||||
|
||||
|
||||
**@mdigger (Dmitry Sedykh)**
|
||||
- Fixes Form binding when content-type is x-www-form-urlencoded
|
||||
- No repeat call c.Writer.Status() in gin.Logger
|
||||
- Fixes Content-Type for json render
|
||||
|
||||
|
||||
**@mirzac (Mirza Ceric)**
|
||||
- Fix debug printing
|
||||
|
||||
|
||||
**@mopemope (Yutaka Matsubara)**
|
||||
- ★ Adds Godep support (Dependencies Manager)
|
||||
- Fix variadic parameter in the flexible render API
|
||||
- Fix Corrupted plain render
|
||||
- Add Pluggable View Renderer Example
|
||||
|
||||
|
||||
**@msemenistyi (Mykyta Semenistyi)**
|
||||
- update Readme.md. Add code to String method
|
||||
|
||||
|
||||
**@msoedov (Sasha Myasoedov)**
|
||||
- ★ Adds tons of unit tests.
|
||||
|
||||
|
||||
**@ngerakines (Nick Gerakines)**
|
||||
- ★ Improves API, c.GET() doesn't panic
|
||||
- Adds MustGet() method
|
||||
|
||||
|
||||
**@r8k (Rajiv Kilaparti)**
|
||||
- Fix Port usage in README.
|
||||
|
||||
|
||||
**@RobAWilkinson (Robert Wilkinson)**
|
||||
- Add example of forms and params
|
||||
|
||||
|
||||
**@se77en (Damon Zhao)**
|
||||
- Improve color logging
|
||||
|
||||
|
||||
**@silasb (Silas Baronda)**
|
||||
- Fixing quotes in README
|
||||
|
||||
|
||||
**@SkuliOskarsson (Skuli Oskarsson)**
|
||||
- Fixes some texts in README II
|
||||
|
||||
|
||||
**@slimmy (Jimmy Pettersson)**
|
||||
- Added messages for required bindings
|
||||
|
||||
|
||||
**@smira (Andrey Smirnov)**
|
||||
- Add support for ignored/unexported fields in binding
|
||||
|
||||
|
||||
**@superalsrk (SRK.Lyu)**
|
||||
- Update httprouter godeps
|
||||
|
||||
|
||||
**@yosssi (Keiji Yoshida)**
|
||||
- Fix link in README
|
||||
|
||||
|
||||
**@yuyabee**
|
||||
- Fixed README
|
||||
64
vendor/github.com/gin-gonic/gin/CHANGELOG.md
generated
vendored
Normal file
64
vendor/github.com/gin-gonic/gin/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
#Changelog
|
||||
|
||||
###Gin 0.6 (Mar 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)
|
||||
|
||||
- [NEW] Development mode
|
||||
- [NEW] Unit tests
|
||||
- [NEW] Add Content.Redirect()
|
||||
- [FIX] Deferring WriteHeader()
|
||||
- [FIX] Improved documentation for model binding
|
||||
|
||||
|
||||
###Gin 0.3 (Jul 18, 2014)
|
||||
|
||||
- [PERFORMANCE] Normal log and error log are printed in the same call.
|
||||
- [PERFORMANCE] Improve performance of NoRouter()
|
||||
- [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
|
||||
- [FIX] Rename NotFound404() to NoRoute()
|
||||
- [FIX] Errors in context are purged
|
||||
- [FIX] Adds HEAD method in Static file serving
|
||||
- [FIX] Refactors Static() file serving
|
||||
- [FIX] Using keyed initialization to fix app-engine integration
|
||||
- [FIX] Can't unmarshal JSON array, #63
|
||||
- [FIX] Renaming Context.Req to Context.Request
|
||||
- [FIX] Check application/x-www-form-urlencoded when parsing form
|
||||
|
||||
|
||||
###Gin 0.2b (Jul 08, 2014)
|
||||
- [PERFORMANCE] Using sync.Pool to allocatio/gc overhead
|
||||
- [NEW] Travis CI integration
|
||||
- [NEW] Completely new logger
|
||||
- [NEW] New API for serving static files. gin.Static()
|
||||
- [NEW] gin.H() can be serialized into XML
|
||||
- [NEW] Typed errors. Errors can be typed. Internet/external/custom.
|
||||
- [NEW] Support for Godeps
|
||||
- [NEW] Travis/Godocs badges in README
|
||||
- [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
|
||||
- [FIX] Tons of README fixes
|
||||
- [FIX] Header is written before body
|
||||
- [FIX] BasicAuth() and changes API a little bit
|
||||
- [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] 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()
|
||||
- [FIX] Add MIT license
|
||||
- [FIX] Changes behaviour of ErrorLogger() and Logger()
|
||||
21
vendor/github.com/gin-gonic/gin/LICENSE
generated
vendored
Normal file
21
vendor/github.com/gin-gonic/gin/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Manuel Martínez-Almeida
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
536
vendor/github.com/gin-gonic/gin/README.md
generated
vendored
Normal file
536
vendor/github.com/gin-gonic/gin/README.md
generated
vendored
Normal file
@@ -0,0 +1,536 @@
|
||||
#Gin Web Framework [](https://godoc.org/github.com/gin-gonic/gin) [](https://travis-ci.org/gin-gonic/gin)
|
||||
|
||||
Gin is a web framework written in 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.
|
||||
|
||||

|
||||
|
||||
```
|
||||
$ cat test.go
|
||||
```
|
||||
```go
|
||||
package main
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.String(200, "hello world")
|
||||
})
|
||||
router.GET("/ping", func(c *gin.Context) {
|
||||
c.String(200, "pong")
|
||||
})
|
||||
router.POST("/submit", func(c *gin.Context) {
|
||||
c.String(401, "not authorized")
|
||||
})
|
||||
router.PUT("/error", func(c *gin.Context) {
|
||||
c.String(500, "and error hapenned :(")
|
||||
})
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
##Gin is new, will it be supported?
|
||||
|
||||
Yes, Gin is an internal tool of [Manu](https://github.com/manucorporat) and [Javi](https://github.com/javierprovecho) for many of our projects/start-ups. We developed it and we are going to continue using and improve it.
|
||||
|
||||
|
||||
##Roadmap for v1.0
|
||||
- [ ] Ask our designer for a cool logo
|
||||
- [ ] Add tons of unit tests
|
||||
- [ ] Add internal benchmarks suite
|
||||
- [ ] More powerful validation API
|
||||
- [ ] Improve documentation
|
||||
- [ ] Add Swagger support
|
||||
- [x] Stable API
|
||||
- [x] Improve logging system
|
||||
- [x] Improve JSON/XML validation using bindings
|
||||
- [x] Improve XML support
|
||||
- [x] Flexible rendering system
|
||||
- [x] Add more cool middlewares, for example redis caching (this also helps developers to understand the framework).
|
||||
- [x] Continuous integration
|
||||
- [x] Performance improments, reduce allocation and garbage collection overhead
|
||||
- [x] Fix bugs
|
||||
|
||||
|
||||
|
||||
## Start using it
|
||||
Obviously, you need to have Git and Go already installed to run Gin.
|
||||
Run this in your terminal
|
||||
|
||||
```
|
||||
go get github.com/gin-gonic/gin
|
||||
```
|
||||
Then import it in your Go code:
|
||||
|
||||
```
|
||||
import "github.com/gin-gonic/gin"
|
||||
```
|
||||
|
||||
|
||||
##Community
|
||||
If you'd like to help out with the project, there's a mailing list and IRC channel where Gin discussions normally happen.
|
||||
|
||||
* IRC
|
||||
* [irc.freenode.net #getgin](irc://irc.freenode.net:6667/getgin)
|
||||
* [Webchat](http://webchat.freenode.net?randomnick=1&channels=%23getgin)
|
||||
* Mailing List
|
||||
* Subscribe: [getgin@librelist.org](mailto:getgin@librelist.org)
|
||||
* [Archives](http://librelist.com/browser/getgin/)
|
||||
|
||||
|
||||
##API Examples
|
||||
|
||||
#### Create most basic PING/PONG HTTP endpoint
|
||||
```go
|
||||
package main
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.String(200, "pong")
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// Creates a gin router + logger and recovery (crash-free) middlewares
|
||||
r := gin.Default()
|
||||
|
||||
r.GET("/someGet", getting)
|
||||
r.POST("/somePost", posting)
|
||||
r.PUT("/somePut", putting)
|
||||
r.DELETE("/someDelete", deleting)
|
||||
r.PATCH("/somePatch", patching)
|
||||
r.HEAD("/someHead", head)
|
||||
r.OPTIONS("/someOptions", options)
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Parameters in path
|
||||
|
||||
```go
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// This handler will match /user/john but will not match neither /user/ or /user
|
||||
r.GET("/user/:name", func(c *gin.Context) {
|
||||
name := c.Params.ByName("name")
|
||||
message := "Hello "+name
|
||||
c.String(200, message)
|
||||
})
|
||||
|
||||
// However, this one will match /user/john/ and also /user/john/send
|
||||
// If no other routers match /user/john, it will redirect to /user/join/
|
||||
r.GET("/user/:name/*action", func(c *gin.Context) {
|
||||
name := c.Params.ByName("name")
|
||||
action := c.Params.ByName("action")
|
||||
message := name + " is " + action
|
||||
c.String(200, message)
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
###Form parameters
|
||||
```go
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// This will respond to urls like search?firstname=Jane&lastname=Doe
|
||||
r.GET("/search", func(c *gin.Context) {
|
||||
// You need to call ParseForm() on the request to receive url and form params first
|
||||
c.Request.ParseForm()
|
||||
|
||||
firstname := c.Request.Form.Get("firstname")
|
||||
lastname := c.Request.Form.get("lastname")
|
||||
|
||||
message := "Hello "+ firstname + lastname
|
||||
c.String(200, message)
|
||||
})
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Grouping routes
|
||||
```go
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// Simple group: v1
|
||||
v1 := r.Group("/v1")
|
||||
{
|
||||
v1.POST("/login", loginEndpoint)
|
||||
v1.POST("/submit", submitEndpoint)
|
||||
v1.POST("/read", readEndpoint)
|
||||
}
|
||||
|
||||
// Simple group: v2
|
||||
v2 := r.Group("/v2")
|
||||
{
|
||||
v2.POST("/login", loginEndpoint)
|
||||
v2.POST("/submit", submitEndpoint)
|
||||
v2.POST("/read", readEndpoint)
|
||||
}
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Blank Gin without middlewares by default
|
||||
|
||||
Use
|
||||
|
||||
```go
|
||||
r := gin.New()
|
||||
```
|
||||
instead of
|
||||
|
||||
```go
|
||||
r := gin.Default()
|
||||
```
|
||||
|
||||
|
||||
#### Using middlewares
|
||||
```go
|
||||
func main() {
|
||||
// Creates a router without any middleware by default
|
||||
r := gin.New()
|
||||
|
||||
// Global middlewares
|
||||
r.Use(gin.Logger())
|
||||
r.Use(gin.Recovery())
|
||||
|
||||
// Per route middlewares, you can add as many as you desire.
|
||||
r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
|
||||
|
||||
// Authorization group
|
||||
// authorized := r.Group("/", AuthRequired())
|
||||
// exactly the same than:
|
||||
authorized := r.Group("/")
|
||||
// per group middlewares! in this case we use the custom created
|
||||
// AuthRequired() middleware just in the "authorized" group.
|
||||
authorized.Use(AuthRequired())
|
||||
{
|
||||
authorized.POST("/login", loginEndpoint)
|
||||
authorized.POST("/submit", submitEndpoint)
|
||||
authorized.POST("/read", readEndpoint)
|
||||
|
||||
// nested group
|
||||
testing := authorized.Group("testing")
|
||||
testing.GET("/analytics", analyticsEndpoint)
|
||||
}
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
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).
|
||||
|
||||
Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`.
|
||||
|
||||
When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use BindWith.
|
||||
|
||||
You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has a empty value when binding, the current request will fail with an error.
|
||||
|
||||
```go
|
||||
// Binding from JSON
|
||||
type LoginJSON struct {
|
||||
User string `json:"user" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// Binding from form values
|
||||
type LoginForm struct {
|
||||
User string `form:"user" binding:"required"`
|
||||
Password string `form:"password" binding:"required"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// Example for binding JSON ({"user": "manu", "password": "123"})
|
||||
r.POST("/loginJSON", func(c *gin.Context) {
|
||||
var json LoginJSON
|
||||
|
||||
c.Bind(&json) // This will infer what binder to use depending on the content-type header.
|
||||
if json.User == "manu" && json.Password == "123" {
|
||||
c.JSON(200, gin.H{"status": "you are logged in"})
|
||||
} else {
|
||||
c.JSON(401, gin.H{"status": "unauthorized"})
|
||||
}
|
||||
})
|
||||
|
||||
// Example for binding a HTML form (user=manu&password=123)
|
||||
r.POST("/loginHTML", func(c *gin.Context) {
|
||||
var form LoginForm
|
||||
|
||||
c.BindWith(&form, binding.Form) // You can also specify which binder to use. We support binding.Form, binding.JSON and binding.XML.
|
||||
if form.User == "manu" && form.Password == "123" {
|
||||
c.JSON(200, gin.H{"status": "you are logged in"})
|
||||
} else {
|
||||
c.JSON(401, gin.H{"status": "unauthorized"})
|
||||
}
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### XML and JSON rendering
|
||||
|
||||
```go
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// gin.H is a shortcut for map[string]interface{}
|
||||
r.GET("/someJSON", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"message": "hey", "status": 200})
|
||||
})
|
||||
|
||||
r.GET("/moreJSON", func(c *gin.Context) {
|
||||
// You also can use a struct
|
||||
var msg struct {
|
||||
Name string `json:"user"`
|
||||
Message string
|
||||
Number int
|
||||
}
|
||||
msg.Name = "Lena"
|
||||
msg.Message = "hey"
|
||||
msg.Number = 123
|
||||
// Note that msg.Name becomes "user" in the JSON
|
||||
// Will output : {"user": "Lena", "Message": "hey", "Number": 123}
|
||||
c.JSON(200, msg)
|
||||
})
|
||||
|
||||
r.GET("/someXML", func(c *gin.Context) {
|
||||
c.XML(200, gin.H{"message": "hey", "status": 200})
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
####Serving static files
|
||||
|
||||
Use Engine.ServeFiles(path string, root http.FileSystem):
|
||||
|
||||
```go
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
r.Static("/assets", "./assets")
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
Note: this will use `httpNotFound` instead of the Router's `NotFound` handler.
|
||||
|
||||
####HTML rendering
|
||||
|
||||
Using LoadHTMLTemplates()
|
||||
|
||||
```go
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
r.LoadHTMLGlob("templates/*")
|
||||
r.GET("/index", func(c *gin.Context) {
|
||||
obj := gin.H{"title": "Main website"}
|
||||
c.HTML(200, "index.tmpl", obj)
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
```html
|
||||
<h1>
|
||||
{{ .title }}
|
||||
</h1>
|
||||
```
|
||||
|
||||
You can also use your own html template render
|
||||
|
||||
```go
|
||||
import "html/template"
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
html := template.Must(template.ParseFiles("file1", "file2"))
|
||||
r.SetHTMLTemplate(html)
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Redirects
|
||||
|
||||
Issuing a HTTP redirect is easy:
|
||||
|
||||
```go
|
||||
r.GET("/test", func(c *gin.Context) {
|
||||
c.Redirect(301, "http://www.google.com/")
|
||||
})
|
||||
```
|
||||
Both internal and external locations are supported.
|
||||
|
||||
|
||||
#### Custom Middlewares
|
||||
|
||||
```go
|
||||
func Logger() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
t := time.Now()
|
||||
|
||||
// Set example variable
|
||||
c.Set("example", "12345")
|
||||
|
||||
// before request
|
||||
|
||||
c.Next()
|
||||
|
||||
// after request
|
||||
latency := time.Since(t)
|
||||
log.Print(latency)
|
||||
|
||||
// access the status we are sending
|
||||
status := c.Writer.Status()
|
||||
log.Println(status)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gin.New()
|
||||
r.Use(Logger())
|
||||
|
||||
r.GET("/test", func(c *gin.Context) {
|
||||
example := c.MustGet("example").(string)
|
||||
|
||||
// it would print: "12345"
|
||||
log.Println(example)
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Using BasicAuth() middleware
|
||||
```go
|
||||
// similate some private data
|
||||
var secrets = gin.H{
|
||||
"foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
|
||||
"austin": gin.H{"email": "austin@example.com", "phone": "666"},
|
||||
"lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// Group using gin.BasicAuth() middleware
|
||||
// gin.Accounts is a shortcut for map[string]string
|
||||
authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
|
||||
"foo": "bar",
|
||||
"austin": "1234",
|
||||
"lena": "hello2",
|
||||
"manu": "4321",
|
||||
}))
|
||||
|
||||
// /admin/secrets endpoint
|
||||
// hit "localhost:8080/admin/secrets
|
||||
authorized.GET("/secrets", func(c *gin.Context) {
|
||||
// get user, it was setted by the BasicAuth middleware
|
||||
user := c.MustGet(gin.AuthUserKey).(string)
|
||||
if secret, ok := secrets[user]; ok {
|
||||
c.JSON(200, gin.H{"user": user, "secret": secret})
|
||||
} else {
|
||||
c.JSON(200, gin.H{"user": user, "secret": "NO SECRET :("})
|
||||
}
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Goroutines inside a middleware
|
||||
When starting inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
r.GET("/long_async", func(c *gin.Context) {
|
||||
// create copy to be used inside the goroutine
|
||||
c_cp := c.Copy()
|
||||
go func() {
|
||||
// simulate a long task with time.Sleep(). 5 seconds
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// note than you are using the copied context "c_cp", IMPORTANT
|
||||
log.Println("Done! in path " + c_cp.Request.URL.Path)
|
||||
}()
|
||||
})
|
||||
|
||||
|
||||
r.GET("/long_sync", func(c *gin.Context) {
|
||||
// simulate a long task with time.Sleep(). 5 seconds
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// since we are NOT using a goroutine, we do not have to copy the context
|
||||
log.Println("Done! in path " + c.Request.URL.Path)
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Custom HTTP configuration
|
||||
|
||||
Use `http.ListenAndServe()` directly, like this:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
http.ListenAndServe(":8080", router)
|
||||
}
|
||||
```
|
||||
or
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
s := &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: router,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
s.ListenAndServe()
|
||||
}
|
||||
```
|
||||
94
vendor/github.com/gin-gonic/gin/auth.go
generated
vendored
Normal file
94
vendor/github.com/gin-gonic/gin/auth.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthUserKey = "user"
|
||||
)
|
||||
|
||||
type (
|
||||
Accounts map[string]string
|
||||
authPair struct {
|
||||
Value string
|
||||
User string
|
||||
}
|
||||
authPairs []authPair
|
||||
)
|
||||
|
||||
func (a authPairs) Len() int { return len(a) }
|
||||
func (a authPairs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a authPairs) Less(i, j int) bool { return a[i].Value < a[j].Value }
|
||||
|
||||
// Implements a basic Basic HTTP Authorization. It takes as argument a map[string]string where
|
||||
// the key is the user name and the value is the password.
|
||||
func BasicAuth(accounts Accounts) HandlerFunc {
|
||||
pairs, err := processAccounts(accounts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return func(c *Context) {
|
||||
// Search user in the slice of allowed credentials
|
||||
user, ok := searchCredential(pairs, c.Request.Header.Get("Authorization"))
|
||||
if !ok {
|
||||
// Credentials doesn't match, we return 401 Unauthorized and abort request.
|
||||
c.Writer.Header().Set("WWW-Authenticate", "Basic realm=\"Authorization Required\"")
|
||||
c.Fail(401, errors.New("Unauthorized"))
|
||||
} else {
|
||||
// user is allowed, set UserId to key "user" in this context, the userId can be read later using
|
||||
// c.Get(gin.AuthUserKey)
|
||||
c.Set(AuthUserKey, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func processAccounts(accounts Accounts) (authPairs, error) {
|
||||
if len(accounts) == 0 {
|
||||
return nil, errors.New("Empty list of authorized credentials")
|
||||
}
|
||||
pairs := make(authPairs, 0, len(accounts))
|
||||
for user, password := range accounts {
|
||||
if len(user) == 0 {
|
||||
return nil, errors.New("User can not be empty")
|
||||
}
|
||||
base := user + ":" + password
|
||||
value := "Basic " + base64.StdEncoding.EncodeToString([]byte(base))
|
||||
pairs = append(pairs, authPair{
|
||||
Value: value,
|
||||
User: user,
|
||||
})
|
||||
}
|
||||
// We have to sort the credentials in order to use bsearch later.
|
||||
sort.Sort(pairs)
|
||||
return pairs, nil
|
||||
}
|
||||
|
||||
func searchCredential(pairs authPairs, auth string) (string, bool) {
|
||||
if len(auth) == 0 {
|
||||
return "", false
|
||||
}
|
||||
// Search user in the slice of allowed credentials
|
||||
r := sort.Search(len(pairs), func(i int) bool { return pairs[i].Value >= auth })
|
||||
if r < len(pairs) && secureCompare(pairs[r].Value, auth) {
|
||||
return pairs[r].User, true
|
||||
} else {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
func secureCompare(given, actual string) bool {
|
||||
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
||||
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
||||
} else {
|
||||
/* Securely compare actual to itself to keep constant time, but always return false */
|
||||
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
||||
}
|
||||
}
|
||||
216
vendor/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
Normal file
216
vendor/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
// 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.
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
Binding interface {
|
||||
Bind(*http.Request, interface{}) error
|
||||
}
|
||||
|
||||
// JSON binding
|
||||
jsonBinding struct{}
|
||||
|
||||
// XML binding
|
||||
xmlBinding struct{}
|
||||
|
||||
// // form binding
|
||||
formBinding struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
JSON = jsonBinding{}
|
||||
XML = xmlBinding{}
|
||||
Form = formBinding{} // todo
|
||||
)
|
||||
|
||||
func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
if err := decoder.Decode(obj); err == nil {
|
||||
return Validate(obj)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
decoder := xml.NewDecoder(req.Body)
|
||||
if err := decoder.Decode(obj); err == nil {
|
||||
return Validate(obj)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (_ formBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mapForm(obj, req.Form); err != nil {
|
||||
return err
|
||||
}
|
||||
return Validate(obj)
|
||||
}
|
||||
|
||||
func mapForm(ptr interface{}, form map[string][]string) error {
|
||||
typ := reflect.TypeOf(ptr).Elem()
|
||||
formStruct := reflect.ValueOf(ptr).Elem()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
typeField := typ.Field(i)
|
||||
if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
|
||||
structField := formStruct.Field(i)
|
||||
if !structField.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
inputValue, exists := form[inputFieldName]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
numElems := len(inputValue)
|
||||
if structField.Kind() == reflect.Slice && numElems > 0 {
|
||||
sliceOf := structField.Type().Elem().Kind()
|
||||
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
||||
for i := 0; i < numElems; i++ {
|
||||
if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
formStruct.Field(i).Set(slice)
|
||||
} else {
|
||||
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
|
||||
switch valueKind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if val == "" {
|
||||
val = "0"
|
||||
}
|
||||
intVal, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
structField.SetInt(int64(intVal))
|
||||
}
|
||||
case reflect.Bool:
|
||||
if val == "" {
|
||||
val = "false"
|
||||
}
|
||||
boolVal, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
structField.SetBool(boolVal)
|
||||
}
|
||||
case reflect.Float32:
|
||||
if val == "" {
|
||||
val = "0.0"
|
||||
}
|
||||
floatVal, err := strconv.ParseFloat(val, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
structField.SetFloat(floatVal)
|
||||
}
|
||||
case reflect.Float64:
|
||||
if val == "" {
|
||||
val = "0.0"
|
||||
}
|
||||
floatVal, err := strconv.ParseFloat(val, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
structField.SetFloat(floatVal)
|
||||
}
|
||||
case reflect.String:
|
||||
structField.SetString(val)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Don't pass in pointers to bind to. Can lead to bugs. See:
|
||||
// https://github.com/codegangsta/martini-contrib/issues/40
|
||||
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
|
||||
func ensureNotPointer(obj interface{}) {
|
||||
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
|
||||
panic("Pointers are not accepted as binding models")
|
||||
}
|
||||
}
|
||||
|
||||
func Validate(obj interface{}, parents ...string) error {
|
||||
typ := reflect.TypeOf(obj)
|
||||
val := reflect.ValueOf(obj)
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
// Allow ignored and unexported fields in the struct
|
||||
if len(field.PkgPath) > 0 || field.Tag.Get("form") == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldValue := val.Field(i).Interface()
|
||||
zero := reflect.Zero(field.Type).Interface()
|
||||
|
||||
if strings.Index(field.Tag.Get("binding"), "required") > -1 {
|
||||
fieldType := field.Type.Kind()
|
||||
if fieldType == reflect.Struct {
|
||||
if reflect.DeepEqual(zero, fieldValue) {
|
||||
return errors.New("Required " + field.Name)
|
||||
}
|
||||
err := Validate(fieldValue, field.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if reflect.DeepEqual(zero, fieldValue) {
|
||||
if len(parents) > 0 {
|
||||
return errors.New("Required " + field.Name + " on " + parents[0])
|
||||
} else {
|
||||
return errors.New("Required " + field.Name)
|
||||
}
|
||||
} else if fieldType == reflect.Slice && field.Type.Elem().Kind() == reflect.Struct {
|
||||
err := Validate(fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
fieldValue := val.Index(i).Interface()
|
||||
err := Validate(fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
434
vendor/github.com/gin-gonic/gin/context.go
generated
vendored
Normal file
434
vendor/github.com/gin-gonic/gin/context.go
generated
vendored
Normal file
@@ -0,0 +1,434 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/gin-gonic/gin/render"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrorTypeInternal = 1 << iota
|
||||
ErrorTypeExternal = 1 << iota
|
||||
ErrorTypeAll = 0xffffffff
|
||||
)
|
||||
|
||||
// Used internally to collect errors that occurred during an http request.
|
||||
type errorMsg struct {
|
||||
Err string `json:"error"`
|
||||
Type uint32 `json:"-"`
|
||||
Meta interface{} `json:"meta"`
|
||||
}
|
||||
|
||||
type errorMsgs []errorMsg
|
||||
|
||||
func (a errorMsgs) ByType(typ uint32) errorMsgs {
|
||||
if len(a) == 0 {
|
||||
return a
|
||||
}
|
||||
result := make(errorMsgs, 0, len(a))
|
||||
for _, msg := range a {
|
||||
if msg.Type&typ > 0 {
|
||||
result = append(result, msg)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (a errorMsgs) String() string {
|
||||
if len(a) == 0 {
|
||||
return ""
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
for i, msg := range a {
|
||||
text := fmt.Sprintf("Error #%02d: %s \n Meta: %v\n", (i + 1), msg.Err, msg.Meta)
|
||||
buffer.WriteString(text)
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Context is the most important part of gin. It allows us to pass variables between middleware,
|
||||
// manage the flow, validate the JSON of a request and render a JSON response for example.
|
||||
type Context struct {
|
||||
writermem responseWriter
|
||||
Request *http.Request
|
||||
Writer ResponseWriter
|
||||
Keys map[string]interface{}
|
||||
Errors errorMsgs
|
||||
Params httprouter.Params
|
||||
Engine *Engine
|
||||
handlers []HandlerFunc
|
||||
index int8
|
||||
accepted []string
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/********** CONTEXT CREATION ********/
|
||||
/************************************/
|
||||
|
||||
func (engine *Engine) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context {
|
||||
c := engine.pool.Get().(*Context)
|
||||
c.writermem.reset(w)
|
||||
c.Request = req
|
||||
c.Params = params
|
||||
c.handlers = handlers
|
||||
c.Keys = nil
|
||||
c.index = -1
|
||||
c.accepted = nil
|
||||
c.Errors = c.Errors[0:0]
|
||||
return c
|
||||
}
|
||||
|
||||
func (engine *Engine) reuseContext(c *Context) {
|
||||
engine.pool.Put(c)
|
||||
}
|
||||
|
||||
func (c *Context) Copy() *Context {
|
||||
var cp Context = *c
|
||||
cp.index = AbortIndex
|
||||
cp.handlers = nil
|
||||
return &cp
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/*************** FLOW ***************/
|
||||
/************************************/
|
||||
|
||||
// Next should be used only in the middlewares.
|
||||
// It executes the pending handlers in the chain inside the calling handler.
|
||||
// See example in github.
|
||||
func (c *Context) Next() {
|
||||
c.index++
|
||||
s := int8(len(c.handlers))
|
||||
for ; c.index < s; c.index++ {
|
||||
c.handlers[c.index](c)
|
||||
}
|
||||
}
|
||||
|
||||
// Forces the system to do not continue calling the pending handlers in the chain.
|
||||
func (c *Context) Abort() {
|
||||
c.index = AbortIndex
|
||||
}
|
||||
|
||||
// Same than AbortWithStatus() but also writes the specified response status code.
|
||||
// For example, the first handler checks if the request is authorized. If it's not, context.AbortWithStatus(401) should be called.
|
||||
func (c *Context) AbortWithStatus(code int) {
|
||||
c.Writer.WriteHeader(code)
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/********* ERROR MANAGEMENT *********/
|
||||
/************************************/
|
||||
|
||||
// Fail is the same as Abort plus an error message.
|
||||
// Calling `context.Fail(500, err)` is equivalent to:
|
||||
// ```
|
||||
// context.Error("Operation aborted", err)
|
||||
// context.AbortWithStatus(500)
|
||||
// ```
|
||||
func (c *Context) Fail(code int, err error) {
|
||||
c.Error(err, "Operation aborted")
|
||||
c.AbortWithStatus(code)
|
||||
}
|
||||
|
||||
func (c *Context) ErrorTyped(err error, typ uint32, meta interface{}) {
|
||||
c.Errors = append(c.Errors, errorMsg{
|
||||
Err: err.Error(),
|
||||
Type: typ,
|
||||
Meta: meta,
|
||||
})
|
||||
}
|
||||
|
||||
// Attaches an error to the current context. The error is pushed to a list of errors.
|
||||
// It's a good idea to call Error for each error that occurred during the resolution of a request.
|
||||
// A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
|
||||
func (c *Context) Error(err error, meta interface{}) {
|
||||
c.ErrorTyped(err, ErrorTypeExternal, meta)
|
||||
}
|
||||
|
||||
func (c *Context) LastError() error {
|
||||
nuErrors := len(c.Errors)
|
||||
if nuErrors > 0 {
|
||||
return errors.New(c.Errors[nuErrors-1].Err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/******** METADATA MANAGEMENT********/
|
||||
/************************************/
|
||||
|
||||
// Sets a new pair key/value just for the specified context.
|
||||
// It also lazy initializes the hashmap.
|
||||
func (c *Context) Set(key string, item interface{}) {
|
||||
if c.Keys == nil {
|
||||
c.Keys = make(map[string]interface{})
|
||||
}
|
||||
c.Keys[key] = item
|
||||
}
|
||||
|
||||
// Get returns the value for the given key or an error if the key does not exist.
|
||||
func (c *Context) Get(key string) (interface{}, error) {
|
||||
if c.Keys != nil {
|
||||
value, ok := c.Keys[key]
|
||||
if ok {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Key does not exist.")
|
||||
}
|
||||
|
||||
// MustGet returns the value for the given key or panics if the value doesn't exist.
|
||||
func (c *Context) MustGet(key string) interface{} {
|
||||
value, err := c.Get(key)
|
||||
if err != nil || value == nil {
|
||||
log.Panicf("Key %s doesn't exist", value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func ipInMasks(ip net.IP, masks []interface{}) bool {
|
||||
for _, proxy := range masks {
|
||||
var mask *net.IPNet
|
||||
var err error
|
||||
|
||||
switch t := proxy.(type) {
|
||||
case string:
|
||||
if _, mask, err = net.ParseCIDR(t); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
case net.IP:
|
||||
mask = &net.IPNet{IP: t, Mask: net.CIDRMask(len(t)*8, len(t)*8)}
|
||||
case net.IPNet:
|
||||
mask = &t
|
||||
}
|
||||
|
||||
if mask.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// the ForwardedFor middleware unwraps the X-Forwarded-For headers, be careful to only use this
|
||||
// middleware if you've got servers in front of this server. The list with (known) proxies and
|
||||
// local ips are being filtered out of the forwarded for list, giving the last not local ip being
|
||||
// the real client ip.
|
||||
func ForwardedFor(proxies ...interface{}) HandlerFunc {
|
||||
if len(proxies) == 0 {
|
||||
// default to local ips
|
||||
var reservedLocalIps = []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}
|
||||
|
||||
proxies = make([]interface{}, len(reservedLocalIps))
|
||||
|
||||
for i, v := range reservedLocalIps {
|
||||
proxies[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
return func(c *Context) {
|
||||
// the X-Forwarded-For header contains an array with left most the client ip, then
|
||||
// comma separated, all proxies the request passed. The last proxy appears
|
||||
// as the remote address of the request. Returning the client
|
||||
// ip to comply with default RemoteAddr response.
|
||||
|
||||
// check if remoteaddr is local ip or in list of defined proxies
|
||||
remoteIp := net.ParseIP(strings.Split(c.Request.RemoteAddr, ":")[0])
|
||||
|
||||
if !ipInMasks(remoteIp, proxies) {
|
||||
return
|
||||
}
|
||||
|
||||
if forwardedFor := c.Request.Header.Get("X-Forwarded-For"); forwardedFor != "" {
|
||||
parts := strings.Split(forwardedFor, ",")
|
||||
|
||||
for i := len(parts) - 1; i >= 0; i-- {
|
||||
part := parts[i]
|
||||
|
||||
ip := net.ParseIP(strings.TrimSpace(part))
|
||||
|
||||
if ipInMasks(ip, proxies) {
|
||||
continue
|
||||
}
|
||||
|
||||
// returning remote addr conform the original remote addr format
|
||||
c.Request.RemoteAddr = ip.String() + ":0"
|
||||
|
||||
// remove forwarded for address
|
||||
c.Request.Header.Set("X-Forwarded-For", "")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) ClientIP() string {
|
||||
return c.Request.RemoteAddr
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/********* PARSING REQUEST **********/
|
||||
/************************************/
|
||||
|
||||
// This function checks the Content-Type to select a binding engine automatically,
|
||||
// Depending the "Content-Type" header different bindings are used:
|
||||
// "application/json" --> JSON binding
|
||||
// "application/xml" --> XML binding
|
||||
// else --> returns an error
|
||||
// if Parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid.
|
||||
func (c *Context) Bind(obj interface{}) bool {
|
||||
var b binding.Binding
|
||||
ctype := filterFlags(c.Request.Header.Get("Content-Type"))
|
||||
switch {
|
||||
case c.Request.Method == "GET" || ctype == MIMEPOSTForm:
|
||||
b = binding.Form
|
||||
case ctype == MIMEJSON:
|
||||
b = binding.JSON
|
||||
case ctype == MIMEXML || ctype == MIMEXML2:
|
||||
b = binding.XML
|
||||
default:
|
||||
c.Fail(400, errors.New("unknown content-type: "+ctype))
|
||||
return false
|
||||
}
|
||||
return c.BindWith(obj, b)
|
||||
}
|
||||
|
||||
func (c *Context) BindWith(obj interface{}, b binding.Binding) bool {
|
||||
if err := b.Bind(c.Request, obj); err != nil {
|
||||
c.Fail(400, err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/******** RESPONSE RENDERING ********/
|
||||
/************************************/
|
||||
|
||||
func (c *Context) Render(code int, render render.Render, obj ...interface{}) {
|
||||
if err := render.Render(c.Writer, code, obj...); err != nil {
|
||||
c.ErrorTyped(err, ErrorTypeInternal, obj)
|
||||
c.AbortWithStatus(500)
|
||||
}
|
||||
}
|
||||
|
||||
// Serializes the given struct as JSON into the response body in a fast and efficient way.
|
||||
// It also sets the Content-Type as "application/json".
|
||||
func (c *Context) JSON(code int, obj interface{}) {
|
||||
c.Render(code, render.JSON, obj)
|
||||
}
|
||||
|
||||
// Serializes the given struct as XML into the response body in a fast and efficient way.
|
||||
// It also sets the Content-Type as "application/xml".
|
||||
func (c *Context) XML(code int, obj interface{}) {
|
||||
c.Render(code, render.XML, obj)
|
||||
}
|
||||
|
||||
// Renders the HTTP template specified by its file name.
|
||||
// It also updates the HTTP code and sets the Content-Type as "text/html".
|
||||
// See http://golang.org/doc/articles/wiki/
|
||||
func (c *Context) HTML(code int, name string, obj interface{}) {
|
||||
c.Render(code, c.Engine.HTMLRender, name, obj)
|
||||
}
|
||||
|
||||
// Writes the given string into the response body and sets the Content-Type to "text/plain".
|
||||
func (c *Context) String(code int, format string, values ...interface{}) {
|
||||
c.Render(code, render.Plain, format, values)
|
||||
}
|
||||
|
||||
// Returns a HTTP redirect to the specific location.
|
||||
func (c *Context) Redirect(code int, location string) {
|
||||
if code >= 300 && code <= 308 {
|
||||
c.Render(code, render.Redirect, location)
|
||||
} else {
|
||||
panic(fmt.Sprintf("Cannot send a redirect with status code %d", code))
|
||||
}
|
||||
}
|
||||
|
||||
// Writes some data into the body stream and updates the HTTP code.
|
||||
func (c *Context) Data(code int, contentType string, data []byte) {
|
||||
if len(contentType) > 0 {
|
||||
c.Writer.Header().Set("Content-Type", contentType)
|
||||
}
|
||||
c.Writer.WriteHeader(code)
|
||||
c.Writer.Write(data)
|
||||
}
|
||||
|
||||
// Writes the specified file into the body stream
|
||||
func (c *Context) File(filepath string) {
|
||||
http.ServeFile(c.Writer, c.Request, filepath)
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/******** CONTENT NEGOTIATION *******/
|
||||
/************************************/
|
||||
|
||||
type Negotiate struct {
|
||||
Offered []string
|
||||
HTMLPath string
|
||||
HTMLData interface{}
|
||||
JSONData interface{}
|
||||
XMLData interface{}
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func (c *Context) Negotiate(code int, config Negotiate) {
|
||||
switch c.NegotiateFormat(config.Offered...) {
|
||||
case MIMEJSON:
|
||||
data := chooseData(config.JSONData, config.Data)
|
||||
c.JSON(code, data)
|
||||
|
||||
case MIMEHTML:
|
||||
data := chooseData(config.HTMLData, config.Data)
|
||||
if len(config.HTMLPath) == 0 {
|
||||
panic("negotiate config is wrong. html path is needed")
|
||||
}
|
||||
c.HTML(code, config.HTMLPath, data)
|
||||
|
||||
case MIMEXML:
|
||||
data := chooseData(config.XMLData, config.Data)
|
||||
c.XML(code, data)
|
||||
|
||||
default:
|
||||
c.Fail(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server"))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) NegotiateFormat(offered ...string) string {
|
||||
if len(offered) == 0 {
|
||||
panic("you must provide at least one offer")
|
||||
}
|
||||
if c.accepted == nil {
|
||||
c.accepted = parseAccept(c.Request.Header.Get("Accept"))
|
||||
}
|
||||
if len(c.accepted) == 0 {
|
||||
return offered[0]
|
||||
|
||||
} else {
|
||||
for _, accepted := range c.accepted {
|
||||
for _, offert := range offered {
|
||||
if accepted == offert {
|
||||
return offert
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) SetAccepted(formats ...string) {
|
||||
c.accepted = formats
|
||||
}
|
||||
47
vendor/github.com/gin-gonic/gin/deprecated.go
generated
vendored
Normal file
47
vendor/github.com/gin-gonic/gin/deprecated.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// DEPRECATED, use Bind() instead.
|
||||
// Like ParseBody() but this method also writes a 400 error if the json is not valid.
|
||||
func (c *Context) EnsureBody(item interface{}) bool {
|
||||
return c.Bind(item)
|
||||
}
|
||||
|
||||
// DEPRECATED use bindings directly
|
||||
// Parses the body content as a JSON input. It decodes the json payload into the struct specified as a pointer.
|
||||
func (c *Context) ParseBody(item interface{}) error {
|
||||
return binding.JSON.Bind(c.Request, item)
|
||||
}
|
||||
|
||||
// DEPRECATED use gin.Static() instead
|
||||
// ServeFiles serves files from the given file system root.
|
||||
// The path must end with "/*filepath", files are then served from the local
|
||||
// path /defined/root/dir/*filepath.
|
||||
// For example if root is "/etc" and *filepath is "passwd", the local file
|
||||
// "/etc/passwd" would be served.
|
||||
// Internally a http.FileServer is used, therefore http.NotFound is used instead
|
||||
// of the Router's NotFound handler.
|
||||
// To use the operating system's file system implementation,
|
||||
// use http.Dir:
|
||||
// router.ServeFiles("/src/*filepath", http.Dir("/var/www"))
|
||||
func (engine *Engine) ServeFiles(path string, root http.FileSystem) {
|
||||
engine.router.ServeFiles(path, root)
|
||||
}
|
||||
|
||||
// DEPRECATED use gin.LoadHTMLGlob() or gin.LoadHTMLFiles() instead
|
||||
func (engine *Engine) LoadHTMLTemplates(pattern string) {
|
||||
engine.LoadHTMLGlob(pattern)
|
||||
}
|
||||
|
||||
// DEPRECATED. Use NoRoute() instead
|
||||
func (engine *Engine) NotFound404(handlers ...HandlerFunc) {
|
||||
engine.NoRoute(handlers...)
|
||||
}
|
||||
143
vendor/github.com/gin-gonic/gin/gin.go
generated
vendored
Normal file
143
vendor/github.com/gin-gonic/gin/gin.go
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin/render"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"html/template"
|
||||
"math"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
AbortIndex = math.MaxInt8 / 2
|
||||
MIMEJSON = "application/json"
|
||||
MIMEHTML = "text/html"
|
||||
MIMEXML = "application/xml"
|
||||
MIMEXML2 = "text/xml"
|
||||
MIMEPlain = "text/plain"
|
||||
MIMEPOSTForm = "application/x-www-form-urlencoded"
|
||||
)
|
||||
|
||||
type (
|
||||
HandlerFunc func(*Context)
|
||||
|
||||
// Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares.
|
||||
Engine struct {
|
||||
*RouterGroup
|
||||
HTMLRender render.Render
|
||||
Default404Body []byte
|
||||
pool sync.Pool
|
||||
allNoRoute []HandlerFunc
|
||||
noRoute []HandlerFunc
|
||||
router *httprouter.Router
|
||||
}
|
||||
)
|
||||
|
||||
// Returns a new blank Engine instance without any middleware attached.
|
||||
// The most basic configuration
|
||||
func New() *Engine {
|
||||
engine := &Engine{}
|
||||
engine.RouterGroup = &RouterGroup{
|
||||
Handlers: nil,
|
||||
absolutePath: "/",
|
||||
engine: engine,
|
||||
}
|
||||
engine.router = httprouter.New()
|
||||
engine.Default404Body = []byte("404 page not found")
|
||||
engine.router.NotFound = engine.handle404
|
||||
engine.pool.New = func() interface{} {
|
||||
c := &Context{Engine: engine}
|
||||
c.Writer = &c.writermem
|
||||
return c
|
||||
}
|
||||
return engine
|
||||
}
|
||||
|
||||
// Returns a Engine instance with the Logger and Recovery already attached.
|
||||
func Default() *Engine {
|
||||
engine := New()
|
||||
engine.Use(Recovery(), Logger())
|
||||
return engine
|
||||
}
|
||||
|
||||
func (engine *Engine) LoadHTMLGlob(pattern string) {
|
||||
if IsDebugging() {
|
||||
render.HTMLDebug.AddGlob(pattern)
|
||||
engine.HTMLRender = render.HTMLDebug
|
||||
} else {
|
||||
templ := template.Must(template.ParseGlob(pattern))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) LoadHTMLFiles(files ...string) {
|
||||
if IsDebugging() {
|
||||
render.HTMLDebug.AddFiles(files...)
|
||||
engine.HTMLRender = render.HTMLDebug
|
||||
} else {
|
||||
templ := template.Must(template.ParseFiles(files...))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
||||
engine.HTMLRender = render.HTMLRender{
|
||||
Template: templ,
|
||||
}
|
||||
}
|
||||
|
||||
// Adds handlers for NoRoute. It return a 404 code by default.
|
||||
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
|
||||
engine.noRoute = handlers
|
||||
engine.rebuild404Handlers()
|
||||
}
|
||||
|
||||
func (engine *Engine) Use(middlewares ...HandlerFunc) {
|
||||
engine.RouterGroup.Use(middlewares...)
|
||||
engine.rebuild404Handlers()
|
||||
}
|
||||
|
||||
func (engine *Engine) rebuild404Handlers() {
|
||||
engine.allNoRoute = engine.combineHandlers(engine.noRoute)
|
||||
}
|
||||
|
||||
func (engine *Engine) handle404(w http.ResponseWriter, req *http.Request) {
|
||||
c := engine.createContext(w, req, nil, engine.allNoRoute)
|
||||
// set 404 by default, useful for logging
|
||||
c.Writer.WriteHeader(404)
|
||||
c.Next()
|
||||
if !c.Writer.Written() {
|
||||
if c.Writer.Status() == 404 {
|
||||
c.Data(-1, MIMEPlain, engine.Default404Body)
|
||||
} else {
|
||||
c.Writer.WriteHeaderNow()
|
||||
}
|
||||
}
|
||||
engine.reuseContext(c)
|
||||
}
|
||||
|
||||
// ServeHTTP makes the router implement the http.Handler interface.
|
||||
func (engine *Engine) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
engine.router.ServeHTTP(writer, request)
|
||||
}
|
||||
|
||||
func (engine *Engine) Run(addr string) error {
|
||||
debugPrint("Listening and serving HTTP on %s\n", addr)
|
||||
if err := http.ListenAndServe(addr, engine); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (engine *Engine) RunTLS(addr string, cert string, key string) error {
|
||||
debugPrint("Listening and serving HTTPS on %s\n", addr)
|
||||
if err := http.ListenAndServeTLS(addr, cert, key, engine); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
105
vendor/github.com/gin-gonic/gin/logger.go
generated
vendored
Normal file
105
vendor/github.com/gin-gonic/gin/logger.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
|
||||
white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
|
||||
yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
|
||||
red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
|
||||
blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109})
|
||||
magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
|
||||
cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
|
||||
reset = string([]byte{27, 91, 48, 109})
|
||||
)
|
||||
|
||||
func ErrorLogger() HandlerFunc {
|
||||
return ErrorLoggerT(ErrorTypeAll)
|
||||
}
|
||||
|
||||
func ErrorLoggerT(typ uint32) HandlerFunc {
|
||||
return func(c *Context) {
|
||||
c.Next()
|
||||
|
||||
errs := c.Errors.ByType(typ)
|
||||
if len(errs) > 0 {
|
||||
// -1 status code = do not change current one
|
||||
c.JSON(-1, c.Errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Logger() HandlerFunc {
|
||||
stdlogger := log.New(os.Stdout, "", 0)
|
||||
//errlogger := log.New(os.Stderr, "", 0)
|
||||
|
||||
return func(c *Context) {
|
||||
// Start timer
|
||||
start := time.Now()
|
||||
|
||||
// Process request
|
||||
c.Next()
|
||||
|
||||
// Stop timer
|
||||
end := time.Now()
|
||||
latency := end.Sub(start)
|
||||
|
||||
clientIP := c.ClientIP()
|
||||
method := c.Request.Method
|
||||
statusCode := c.Writer.Status()
|
||||
statusColor := colorForStatus(statusCode)
|
||||
methodColor := colorForMethod(method)
|
||||
|
||||
stdlogger.Printf("[GIN] %v |%s %3d %s| %12v | %s |%s %s %-7s %s\n%s",
|
||||
end.Format("2006/01/02 - 15:04:05"),
|
||||
statusColor, statusCode, reset,
|
||||
latency,
|
||||
clientIP,
|
||||
methodColor, reset, method,
|
||||
c.Request.URL.Path,
|
||||
c.Errors.String(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func colorForStatus(code int) string {
|
||||
switch {
|
||||
case code >= 200 && code <= 299:
|
||||
return green
|
||||
case code >= 300 && code <= 399:
|
||||
return white
|
||||
case code >= 400 && code <= 499:
|
||||
return yellow
|
||||
default:
|
||||
return red
|
||||
}
|
||||
}
|
||||
|
||||
func colorForMethod(method string) string {
|
||||
switch {
|
||||
case method == "GET":
|
||||
return blue
|
||||
case method == "POST":
|
||||
return cyan
|
||||
case method == "PUT":
|
||||
return yellow
|
||||
case method == "DELETE":
|
||||
return red
|
||||
case method == "PATCH":
|
||||
return green
|
||||
case method == "HEAD":
|
||||
return magenta
|
||||
case method == "OPTIONS":
|
||||
return white
|
||||
default:
|
||||
return reset
|
||||
}
|
||||
}
|
||||
63
vendor/github.com/gin-gonic/gin/mode.go
generated
vendored
Normal file
63
vendor/github.com/gin-gonic/gin/mode.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const GIN_MODE = "GIN_MODE"
|
||||
|
||||
const (
|
||||
DebugMode string = "debug"
|
||||
ReleaseMode string = "release"
|
||||
TestMode string = "test"
|
||||
)
|
||||
const (
|
||||
debugCode = iota
|
||||
releaseCode = iota
|
||||
testCode = iota
|
||||
)
|
||||
|
||||
var gin_mode int = debugCode
|
||||
var mode_name string = DebugMode
|
||||
|
||||
func init() {
|
||||
value := os.Getenv(GIN_MODE)
|
||||
if len(value) == 0 {
|
||||
SetMode(DebugMode)
|
||||
} else {
|
||||
SetMode(value)
|
||||
}
|
||||
}
|
||||
|
||||
func SetMode(value string) {
|
||||
switch value {
|
||||
case DebugMode:
|
||||
gin_mode = debugCode
|
||||
case ReleaseMode:
|
||||
gin_mode = releaseCode
|
||||
case TestMode:
|
||||
gin_mode = testCode
|
||||
default:
|
||||
panic("gin mode unknown: " + value)
|
||||
}
|
||||
mode_name = value
|
||||
}
|
||||
|
||||
func Mode() string {
|
||||
return mode_name
|
||||
}
|
||||
|
||||
func IsDebugging() bool {
|
||||
return gin_mode == debugCode
|
||||
}
|
||||
|
||||
func debugPrint(format string, values ...interface{}) {
|
||||
if IsDebugging() {
|
||||
fmt.Printf("[GIN-debug] "+format, values...)
|
||||
}
|
||||
}
|
||||
98
vendor/github.com/gin-gonic/gin/recovery.go
generated
vendored
Normal file
98
vendor/github.com/gin-gonic/gin/recovery.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
dunno = []byte("???")
|
||||
centerDot = []byte("·")
|
||||
dot = []byte(".")
|
||||
slash = []byte("/")
|
||||
)
|
||||
|
||||
// stack returns a nicely formated stack frame, skipping skip frames
|
||||
func stack(skip int) []byte {
|
||||
buf := new(bytes.Buffer) // the returned data
|
||||
// As we loop, we open files and read them. These variables record the currently
|
||||
// loaded file.
|
||||
var lines [][]byte
|
||||
var lastFile string
|
||||
for i := skip; ; i++ { // Skip the expected number of frames
|
||||
pc, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
// Print this much at least. If we can't find the source, it won't show.
|
||||
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
|
||||
if file != lastFile {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
lines = bytes.Split(data, []byte{'\n'})
|
||||
lastFile = file
|
||||
}
|
||||
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// source returns a space-trimmed slice of the n'th line.
|
||||
func source(lines [][]byte, n int) []byte {
|
||||
n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
|
||||
if n < 0 || n >= len(lines) {
|
||||
return dunno
|
||||
}
|
||||
return bytes.TrimSpace(lines[n])
|
||||
}
|
||||
|
||||
// function returns, if possible, the name of the function containing the PC.
|
||||
func function(pc uintptr) []byte {
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
return dunno
|
||||
}
|
||||
name := []byte(fn.Name())
|
||||
// The name includes the path name to the package, which is unnecessary
|
||||
// since the file name is already included. Plus, it has center dots.
|
||||
// That is, we see
|
||||
// runtime/debug.*T·ptrmethod
|
||||
// and want
|
||||
// *T.ptrmethod
|
||||
// Also the package path might contains dot (e.g. code.google.com/...),
|
||||
// so first eliminate the path prefix
|
||||
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
|
||||
name = name[lastslash+1:]
|
||||
}
|
||||
if period := bytes.Index(name, dot); period >= 0 {
|
||||
name = name[period+1:]
|
||||
}
|
||||
name = bytes.Replace(name, centerDot, dot, -1)
|
||||
return name
|
||||
}
|
||||
|
||||
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
|
||||
// While Martini is in development mode, Recovery will also output the panic as HTML.
|
||||
func Recovery() HandlerFunc {
|
||||
return func(c *Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
stack := stack(3)
|
||||
log.Printf("PANIC: %s\n%s", err, stack)
|
||||
c.Writer.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
123
vendor/github.com/gin-gonic/gin/render/render.go
generated
vendored
Normal file
123
vendor/github.com/gin-gonic/gin/render/render.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// 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.
|
||||
|
||||
package render
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
Render interface {
|
||||
Render(http.ResponseWriter, int, ...interface{}) error
|
||||
}
|
||||
|
||||
// JSON binding
|
||||
jsonRender struct{}
|
||||
|
||||
// XML binding
|
||||
xmlRender struct{}
|
||||
|
||||
// Plain text
|
||||
plainRender struct{}
|
||||
|
||||
// Redirects
|
||||
redirectRender struct{}
|
||||
|
||||
// Redirects
|
||||
htmlDebugRender struct {
|
||||
files []string
|
||||
globs []string
|
||||
}
|
||||
|
||||
// form binding
|
||||
HTMLRender struct {
|
||||
Template *template.Template
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
JSON = jsonRender{}
|
||||
XML = xmlRender{}
|
||||
Plain = plainRender{}
|
||||
Redirect = redirectRender{}
|
||||
HTMLDebug = &htmlDebugRender{}
|
||||
)
|
||||
|
||||
func writeHeader(w http.ResponseWriter, code int, contentType string) {
|
||||
w.Header().Set("Content-Type", contentType+"; charset=utf-8")
|
||||
w.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (_ jsonRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
writeHeader(w, code, "application/json")
|
||||
encoder := json.NewEncoder(w)
|
||||
return encoder.Encode(data[0])
|
||||
}
|
||||
|
||||
func (_ redirectRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
w.Header().Set("Location", data[0].(string))
|
||||
w.WriteHeader(code)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ xmlRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
writeHeader(w, code, "application/xml")
|
||||
encoder := xml.NewEncoder(w)
|
||||
return encoder.Encode(data[0])
|
||||
}
|
||||
|
||||
func (_ plainRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
writeHeader(w, code, "text/plain")
|
||||
format := data[0].(string)
|
||||
args := data[1].([]interface{})
|
||||
var err error
|
||||
if len(args) > 0 {
|
||||
_, err = w.Write([]byte(fmt.Sprintf(format, args...)))
|
||||
} else {
|
||||
_, err = w.Write([]byte(format))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *htmlDebugRender) AddGlob(pattern string) {
|
||||
r.globs = append(r.globs, pattern)
|
||||
}
|
||||
|
||||
func (r *htmlDebugRender) AddFiles(files ...string) {
|
||||
r.files = append(r.files, files...)
|
||||
}
|
||||
|
||||
func (r *htmlDebugRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
writeHeader(w, code, "text/html")
|
||||
file := data[0].(string)
|
||||
obj := data[1]
|
||||
|
||||
t := template.New("")
|
||||
|
||||
if len(r.files) > 0 {
|
||||
if _, err := t.ParseFiles(r.files...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, glob := range r.globs {
|
||||
if _, err := t.ParseGlob(glob); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return t.ExecuteTemplate(w, file, obj)
|
||||
}
|
||||
|
||||
func (html HTMLRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
writeHeader(w, code, "text/html")
|
||||
file := data[0].(string)
|
||||
obj := data[1]
|
||||
return html.Template.ExecuteTemplate(w, file, obj)
|
||||
}
|
||||
100
vendor/github.com/gin-gonic/gin/response_writer.go
generated
vendored
Normal file
100
vendor/github.com/gin-gonic/gin/response_writer.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
NoWritten = -1
|
||||
)
|
||||
|
||||
type (
|
||||
ResponseWriter interface {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Flusher
|
||||
http.CloseNotifier
|
||||
|
||||
Status() int
|
||||
Size() int
|
||||
Written() bool
|
||||
WriteHeaderNow()
|
||||
}
|
||||
|
||||
responseWriter struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
size int
|
||||
}
|
||||
)
|
||||
|
||||
func (w *responseWriter) reset(writer http.ResponseWriter) {
|
||||
w.ResponseWriter = writer
|
||||
w.status = 200
|
||||
w.size = NoWritten
|
||||
}
|
||||
|
||||
func (w *responseWriter) WriteHeader(code int) {
|
||||
if code > 0 {
|
||||
w.status = code
|
||||
if w.Written() {
|
||||
log.Println("[GIN] WARNING. Headers were already written!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *responseWriter) WriteHeaderNow() {
|
||||
if !w.Written() {
|
||||
w.size = 0
|
||||
w.ResponseWriter.WriteHeader(w.status)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *responseWriter) Write(data []byte) (n int, err error) {
|
||||
w.WriteHeaderNow()
|
||||
n, err = w.ResponseWriter.Write(data)
|
||||
w.size += n
|
||||
return
|
||||
}
|
||||
|
||||
func (w *responseWriter) Status() int {
|
||||
return w.status
|
||||
}
|
||||
|
||||
func (w *responseWriter) Size() int {
|
||||
return w.size
|
||||
}
|
||||
|
||||
func (w *responseWriter) Written() bool {
|
||||
return w.size != NoWritten
|
||||
}
|
||||
|
||||
// Implements the http.Hijacker interface
|
||||
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
hijacker, ok := w.ResponseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, errors.New("the ResponseWriter doesn't support the Hijacker interface")
|
||||
}
|
||||
return hijacker.Hijack()
|
||||
}
|
||||
|
||||
// Implements the http.CloseNotify interface
|
||||
func (w *responseWriter) CloseNotify() <-chan bool {
|
||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// Implements the http.Flush interface
|
||||
func (w *responseWriter) Flush() {
|
||||
flusher, ok := w.ResponseWriter.(http.Flusher)
|
||||
if ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
148
vendor/github.com/gin-gonic/gin/routergroup.go
generated
vendored
Normal file
148
vendor/github.com/gin-gonic/gin/routergroup.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Used internally to configure router, a RouterGroup is associated with a prefix
|
||||
// and an array of handlers (middlewares)
|
||||
type RouterGroup struct {
|
||||
Handlers []HandlerFunc
|
||||
absolutePath string
|
||||
engine *Engine
|
||||
}
|
||||
|
||||
// Adds middlewares to the group, see example code in github.
|
||||
func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
|
||||
group.Handlers = append(group.Handlers, middlewares...)
|
||||
}
|
||||
|
||||
// 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 *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
|
||||
return &RouterGroup{
|
||||
Handlers: group.combineHandlers(handlers),
|
||||
absolutePath: group.calculateAbsolutePath(relativePath),
|
||||
engine: group.engine,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle registers a new request handle and middlewares with the given path and method.
|
||||
// The last handler should be the real handler, the other ones should be middlewares that can and should be shared among different routes.
|
||||
// See the example code in github.
|
||||
//
|
||||
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
|
||||
// functions can be used.
|
||||
//
|
||||
// This function is intended for bulk loading and to allow the usage of less
|
||||
// frequently used, non-standardized or custom methods (e.g. for internal
|
||||
// communication with a proxy).
|
||||
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers []HandlerFunc) {
|
||||
absolutePath := group.calculateAbsolutePath(relativePath)
|
||||
handlers = group.combineHandlers(handlers)
|
||||
if IsDebugging() {
|
||||
nuHandlers := len(handlers)
|
||||
handlerName := nameOfFunction(handlers[nuHandlers-1])
|
||||
debugPrint("%-5s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
|
||||
}
|
||||
|
||||
group.engine.router.Handle(httpMethod, absolutePath, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||
context := group.engine.createContext(w, req, params, handlers)
|
||||
context.Next()
|
||||
context.Writer.WriteHeaderNow()
|
||||
group.engine.reuseContext(context)
|
||||
})
|
||||
}
|
||||
|
||||
// POST is a shortcut for router.Handle("POST", path, handle)
|
||||
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) {
|
||||
group.Handle("POST", relativePath, handlers)
|
||||
}
|
||||
|
||||
// GET is a shortcut for router.Handle("GET", path, handle)
|
||||
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) {
|
||||
group.Handle("GET", relativePath, handlers)
|
||||
}
|
||||
|
||||
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
|
||||
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) {
|
||||
group.Handle("DELETE", relativePath, handlers)
|
||||
}
|
||||
|
||||
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
|
||||
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) {
|
||||
group.Handle("PATCH", relativePath, handlers)
|
||||
}
|
||||
|
||||
// PUT is a shortcut for router.Handle("PUT", path, handle)
|
||||
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) {
|
||||
group.Handle("PUT", relativePath, handlers)
|
||||
}
|
||||
|
||||
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
|
||||
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) {
|
||||
group.Handle("OPTIONS", relativePath, handlers)
|
||||
}
|
||||
|
||||
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
|
||||
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) {
|
||||
group.Handle("HEAD", relativePath, handlers)
|
||||
}
|
||||
|
||||
// LINK is a shortcut for router.Handle("LINK", path, handle)
|
||||
func (group *RouterGroup) LINK(relativePath string, handlers ...HandlerFunc) {
|
||||
group.Handle("LINK", relativePath, handlers)
|
||||
}
|
||||
|
||||
// UNLINK is a shortcut for router.Handle("UNLINK", path, handle)
|
||||
func (group *RouterGroup) UNLINK(relativePath string, handlers ...HandlerFunc) {
|
||||
group.Handle("UNLINK", relativePath, handlers)
|
||||
}
|
||||
|
||||
// Static serves files from the given file system root.
|
||||
// Internally a http.FileServer is used, therefore http.NotFound is used instead
|
||||
// of the Router's NotFound handler.
|
||||
// To use the operating system's file system implementation,
|
||||
// use :
|
||||
// router.Static("/static", "/var/www")
|
||||
func (group *RouterGroup) Static(relativePath, root string) {
|
||||
absolutePath := group.calculateAbsolutePath(relativePath)
|
||||
handler := group.createStaticHandler(absolutePath, root)
|
||||
absolutePath = path.Join(absolutePath, "/*filepath")
|
||||
|
||||
// Register GET and HEAD handlers
|
||||
group.GET(absolutePath, handler)
|
||||
group.HEAD(absolutePath, handler)
|
||||
}
|
||||
|
||||
func (group *RouterGroup) createStaticHandler(absolutePath, root string) func(*Context) {
|
||||
fileServer := http.StripPrefix(absolutePath, http.FileServer(http.Dir(root)))
|
||||
return func(c *Context) {
|
||||
fileServer.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func (group *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
|
||||
finalSize := len(group.Handlers) + len(handlers)
|
||||
mergedHandlers := make([]HandlerFunc, 0, finalSize)
|
||||
mergedHandlers = append(mergedHandlers, group.Handlers...)
|
||||
return append(mergedHandlers, handlers...)
|
||||
}
|
||||
|
||||
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
|
||||
if len(relativePath) == 0 {
|
||||
return group.absolutePath
|
||||
}
|
||||
absolutePath := path.Join(group.absolutePath, relativePath)
|
||||
appendSlash := lastChar(relativePath) == '/' && lastChar(absolutePath) != '/'
|
||||
if appendSlash {
|
||||
return absolutePath + "/"
|
||||
}
|
||||
return absolutePath
|
||||
}
|
||||
82
vendor/github.com/gin-gonic/gin/utils.go
generated
vendored
Normal file
82
vendor/github.com/gin-gonic/gin/utils.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type H map[string]interface{}
|
||||
|
||||
// Allows type H to be used with xml.Marshal
|
||||
func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
start.Name = xml.Name{
|
||||
Space: "",
|
||||
Local: "map",
|
||||
}
|
||||
if err := e.EncodeToken(start); err != nil {
|
||||
return err
|
||||
}
|
||||
for key, value := range h {
|
||||
elem := xml.StartElement{
|
||||
Name: xml.Name{Space: "", Local: key},
|
||||
Attr: []xml.Attr{},
|
||||
}
|
||||
if err := e.EncodeElement(value, elem); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterFlags(content string) string {
|
||||
for i, char := range content {
|
||||
if char == ' ' || char == ';' {
|
||||
return content[:i]
|
||||
}
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
func chooseData(custom, wildcard interface{}) interface{} {
|
||||
if custom == nil {
|
||||
if wildcard == nil {
|
||||
panic("negotiation config is invalid")
|
||||
}
|
||||
return wildcard
|
||||
}
|
||||
return custom
|
||||
}
|
||||
|
||||
func parseAccept(accept string) []string {
|
||||
parts := strings.Split(accept, ",")
|
||||
for i, part := range parts {
|
||||
index := strings.IndexByte(part, ';')
|
||||
if index >= 0 {
|
||||
part = part[0:index]
|
||||
}
|
||||
part = strings.TrimSpace(part)
|
||||
parts[i] = part
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
func lastChar(str string) uint8 {
|
||||
size := len(str)
|
||||
if size == 0 {
|
||||
panic("The length of the string can't be 0")
|
||||
}
|
||||
return str[size-1]
|
||||
}
|
||||
|
||||
func nameOfFunction(f interface{}) string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
|
||||
}
|
||||
Reference in New Issue
Block a user