2014-06-18 03:42:34 +04:00
package gin
import (
2014-07-02 04:31:11 +04:00
"bytes"
2014-06-18 03:42:34 +04:00
"encoding/json"
"encoding/xml"
2014-07-02 04:32:44 +04:00
"errors"
2014-07-02 04:31:11 +04:00
"fmt"
2014-07-03 21:19:06 +04:00
"github.com/gin-gonic/gin/binding"
2014-06-18 03:42:34 +04:00
"github.com/julienschmidt/httprouter"
"html/template"
"log"
"math"
"net/http"
"path"
)
const (
AbortIndex = math . MaxInt8 / 2
2014-07-05 01:28:50 +04:00
MIMEJSON = "application/json"
MIMEHTML = "text/html"
MIMEXML = "application/xml"
MIMEXML2 = "text/xml"
MIMEPlain = "text/plain"
2014-06-18 03:42:34 +04:00
)
2014-07-08 02:16:41 +04:00
const (
ErrorTypeInternal = 1 << iota
ErrorTypeExternal = 1 << iota
ErrorTypeAll = 0xffffffff
)
2014-06-18 03:42:34 +04:00
type (
HandlerFunc func ( * Context )
H map [ string ] interface { }
2014-07-03 22:30:01 +04:00
// Used internally to collect errors that occurred during an http request.
2014-07-08 02:16:41 +04:00
errorMsg struct {
2014-07-02 04:31:11 +04:00
Err string ` json:"error" `
2014-07-08 02:16:41 +04:00
Type uint32 ` json:"-" `
2014-07-02 04:31:11 +04:00
Meta interface { } ` json:"meta" `
2014-06-18 03:42:34 +04:00
}
2014-07-08 02:16:41 +04:00
errorMsgs [ ] errorMsg
2014-07-02 04:31:11 +04:00
2014-07-03 16:11:42 +04:00
Config struct {
CacheSize int
Preallocated int
}
2014-06-18 03:42:34 +04:00
// 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.
Context struct {
Req * http . Request
2014-07-04 02:01:28 +04:00
Writer ResponseWriter
2014-06-18 03:42:34 +04:00
Keys map [ string ] interface { }
2014-07-08 02:16:41 +04:00
Errors errorMsgs
2014-06-18 03:42:34 +04:00
Params httprouter . Params
2014-07-03 16:11:42 +04:00
Engine * Engine
2014-06-18 03:42:34 +04:00
handlers [ ] HandlerFunc
index int8
}
// Used internally to configure router, a RouterGroup is associated with a prefix
// and an array of handlers (middlewares)
RouterGroup struct {
Handlers [ ] HandlerFunc
prefix string
parent * RouterGroup
engine * Engine
}
2014-07-03 22:30:01 +04:00
// Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares.
2014-06-18 03:42:34 +04:00
Engine struct {
* RouterGroup
2014-07-02 22:17:57 +04:00
HTMLTemplates * template . Template
cache chan * Context
2014-06-18 03:42:34 +04:00
handlers404 [ ] HandlerFunc
router * httprouter . Router
}
)
2014-07-04 18:59:57 +04:00
var (
DefaultConfig = Config {
CacheSize : 1024 ,
Preallocated : 512 ,
}
)
2014-07-05 04:51:25 +04:00
// Allows type H to be used with xml.Marshal
func ( h H ) MarshalXML ( e * xml . Encoder , start xml . StartElement ) error {
2014-07-06 20:25:17 +04:00
start . Name = xml . Name { "" , "map" }
2014-07-05 07:18:05 +04:00
if err := e . EncodeToken ( start ) ; err != nil {
return err
}
2014-07-05 04:51:25 +04:00
for key , value := range h {
2014-07-05 07:18:05 +04:00
elem := xml . StartElement {
xml . Name { "" , key } ,
[ ] xml . Attr { } ,
}
2014-07-06 20:25:17 +04:00
if err := e . EncodeElement ( value , elem ) ; err != nil {
2014-07-05 07:18:05 +04:00
return err
}
}
2014-07-06 20:25:17 +04:00
if err := e . EncodeToken ( xml . EndElement { start . Name } ) ; err != nil {
2014-07-05 07:18:05 +04:00
return err
2014-07-05 04:51:25 +04:00
}
return nil
}
2014-07-08 02:16:41 +04:00
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 {
2014-07-02 04:31:11 +04:00
var buffer bytes . Buffer
for i , msg := range a {
2014-07-04 06:31:11 +04:00
text := fmt . Sprintf ( "Error #%02d: %s \n Meta: %v\n" , ( i + 1 ) , msg . Err , msg . Meta )
2014-07-02 04:31:11 +04:00
buffer . WriteString ( text )
}
return buffer . String ( )
}
2014-07-03 16:11:42 +04:00
func NewWithConfig ( config Config ) * Engine {
if config . CacheSize < 2 {
panic ( "CacheSize must be at least 2" )
}
if config . Preallocated > config . CacheSize {
panic ( "Preallocated must be less or equal to CacheSize" )
}
2014-06-18 03:42:34 +04:00
engine := & Engine { }
2014-07-02 13:09:04 +04:00
engine . RouterGroup = & RouterGroup { nil , "/" , nil , engine }
2014-06-18 03:42:34 +04:00
engine . router = httprouter . New ( )
engine . router . NotFound = engine . handle404
2014-07-03 16:11:42 +04:00
engine . cache = make ( chan * Context , config . CacheSize )
2014-07-02 22:17:57 +04:00
// Fill it with empty contexts
2014-07-03 16:11:42 +04:00
for i := 0 ; i < config . Preallocated ; i ++ {
2014-07-04 02:01:28 +04:00
engine . cache <- & Context { Engine : engine , Writer : & responseWriter { } }
2014-07-02 22:17:57 +04:00
}
2014-06-18 03:42:34 +04:00
return engine
}
2014-07-03 16:11:42 +04:00
// Returns a new blank Engine instance without any middleware attached.
// The most basic configuration
func New ( ) * Engine {
2014-07-04 18:59:57 +04:00
return NewWithConfig ( DefaultConfig )
2014-07-03 16:11:42 +04:00
}
2014-06-18 03:42:34 +04:00
// 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 ) LoadHTMLTemplates ( pattern string ) {
engine . HTMLTemplates = template . Must ( template . ParseGlob ( pattern ) )
}
// Adds handlers for NotFound. It return a 404 code by default.
func ( engine * Engine ) NotFound404 ( handlers ... HandlerFunc ) {
engine . handlers404 = handlers
}
2014-07-03 16:11:42 +04:00
func ( engine * Engine ) CacheStress ( ) float32 {
return 1.0 - float32 ( len ( engine . cache ) ) / float32 ( cap ( engine . cache ) )
}
2014-06-18 03:42:34 +04:00
func ( engine * Engine ) handle404 ( w http . ResponseWriter , req * http . Request ) {
2014-06-30 05:59:00 +04:00
handlers := engine . combineHandlers ( engine . handlers404 )
2014-06-18 03:42:34 +04:00
c := engine . createContext ( w , req , nil , handlers )
2014-07-04 02:12:35 +04:00
c . Writer . setStatus ( 404 )
2014-06-30 05:59:00 +04:00
c . Next ( )
2014-07-04 02:12:35 +04:00
if ! c . Writer . Written ( ) {
2014-07-05 21:23:40 +04:00
c . Data ( 404 , MIMEPlain , [ ] byte ( "404 page not found" ) )
2014-07-04 02:12:35 +04:00
}
2014-07-02 22:17:57 +04:00
engine . reuseContext ( c )
2014-06-18 03:42:34 +04:00
}
// ServeHTTP makes the router implement the http.Handler interface.
func ( engine * Engine ) ServeHTTP ( w http . ResponseWriter , req * http . Request ) {
engine . router . ServeHTTP ( w , req )
}
func ( engine * Engine ) Run ( addr string ) {
2014-07-03 01:10:30 +04:00
if err := http . ListenAndServe ( addr , engine ) ; err != nil {
panic ( err )
}
2014-06-18 03:42:34 +04:00
}
/************************************/
/********** ROUTES GROUPING *********/
/************************************/
2014-07-02 22:17:57 +04:00
func ( engine * Engine ) createContext ( w http . ResponseWriter , req * http . Request , params httprouter . Params , handlers [ ] HandlerFunc ) * Context {
select {
case c := <- engine . cache :
2014-07-04 02:01:28 +04:00
c . Writer . reset ( w )
2014-07-02 22:17:57 +04:00
c . Req = req
c . Params = params
c . handlers = handlers
2014-07-03 18:31:27 +04:00
c . Keys = nil
2014-07-02 22:17:57 +04:00
c . index = - 1
return c
default :
return & Context {
2014-07-04 02:01:28 +04:00
Writer : & responseWriter { w , - 1 , false } ,
2014-07-02 22:17:57 +04:00
Req : req ,
Params : params ,
handlers : handlers ,
index : - 1 ,
2014-07-03 16:11:42 +04:00
Engine : engine ,
2014-07-02 22:17:57 +04:00
}
}
}
func ( engine * Engine ) reuseContext ( c * Context ) {
2014-07-03 18:31:27 +04:00
select {
case engine . cache <- c :
default :
2014-06-18 03:42:34 +04:00
}
}
// Adds middlewares to the group, see example code in github.
func ( group * RouterGroup ) Use ( middlewares ... HandlerFunc ) {
group . Handlers = append ( group . Handlers , middlewares ... )
}
2014-07-03 22:30:01 +04:00
// Creates a new router group. You should add all the routes that have common middlwares or the same path prefix.
2014-06-18 03:42:34 +04:00
// For example, all the routes that use a common middlware for authorization could be grouped.
func ( group * RouterGroup ) Group ( component string , handlers ... HandlerFunc ) * RouterGroup {
prefix := path . Join ( group . prefix , component )
return & RouterGroup {
2014-06-30 05:59:00 +04:00
Handlers : group . combineHandlers ( handlers ) ,
2014-06-18 03:42:34 +04:00
parent : group ,
prefix : prefix ,
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 ( method , p string , handlers [ ] HandlerFunc ) {
p = path . Join ( group . prefix , p )
2014-06-30 05:59:00 +04:00
handlers = group . combineHandlers ( handlers )
2014-06-18 03:42:34 +04:00
group . engine . router . Handle ( method , p , func ( w http . ResponseWriter , req * http . Request , params httprouter . Params ) {
2014-07-02 22:17:57 +04:00
c := group . engine . createContext ( w , req , params , handlers )
c . Next ( )
group . engine . reuseContext ( c )
2014-06-18 03:42:34 +04:00
} )
}
// POST is a shortcut for router.Handle("POST", path, handle)
func ( group * RouterGroup ) POST ( path string , handlers ... HandlerFunc ) {
group . Handle ( "POST" , path , handlers )
}
// GET is a shortcut for router.Handle("GET", path, handle)
func ( group * RouterGroup ) GET ( path string , handlers ... HandlerFunc ) {
group . Handle ( "GET" , path , handlers )
}
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
func ( group * RouterGroup ) DELETE ( path string , handlers ... HandlerFunc ) {
group . Handle ( "DELETE" , path , handlers )
}
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
func ( group * RouterGroup ) PATCH ( path string , handlers ... HandlerFunc ) {
group . Handle ( "PATCH" , path , handlers )
}
// PUT is a shortcut for router.Handle("PUT", path, handle)
func ( group * RouterGroup ) PUT ( path string , handlers ... HandlerFunc ) {
group . Handle ( "PUT" , path , handlers )
}
2014-07-03 18:14:33 +04:00
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
func ( group * RouterGroup ) OPTIONS ( path string , handlers ... HandlerFunc ) {
group . Handle ( "OPTIONS" , path , handlers )
2014-06-18 03:42:34 +04:00
}
2014-07-03 18:57:45 +04:00
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
func ( group * RouterGroup ) HEAD ( path string , handlers ... HandlerFunc ) {
group . Handle ( "HEAD" , path , handlers )
}
2014-07-08 03:09:48 +04:00
// 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 ( p , root string ) {
p = path . Join ( p , "/*filepath" )
fileServer := http . FileServer ( http . Dir ( root ) )
group . GET ( p , func ( c * Context ) {
original := c . Req . URL . Path
c . Req . URL . Path = c . Params . ByName ( "filepath" )
fileServer . ServeHTTP ( c . Writer , c . Req )
c . Req . URL . Path = original
} )
}
2014-06-30 05:59:00 +04:00
func ( group * RouterGroup ) combineHandlers ( handlers [ ] HandlerFunc ) [ ] HandlerFunc {
s := len ( group . Handlers ) + len ( handlers )
h := make ( [ ] HandlerFunc , 0 , s )
h = append ( h , group . Handlers ... )
h = append ( h , handlers ... )
return h
2014-06-18 03:42:34 +04:00
}
/************************************/
/****** FLOW AND ERROR MANAGEMENT****/
/************************************/
2014-07-03 18:31:27 +04:00
func ( c * Context ) Copy ( ) * Context {
2014-07-03 18:35:20 +04:00
var cp Context = * c
2014-07-03 18:31:27 +04:00
cp . index = AbortIndex
cp . handlers = nil
2014-07-03 18:35:20 +04:00
return & cp
2014-07-03 17:59:39 +04:00
}
2014-06-18 03:42:34 +04:00
// 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.
// For example, the first handler checks if the request is authorized. If it's not, context.Abort(401) should be called.
// The rest of pending handlers would never be called for that request.
func ( c * Context ) Abort ( code int ) {
2014-07-04 02:37:54 +04:00
if code >= 0 {
c . Writer . WriteHeader ( code )
}
2014-06-18 03:42:34 +04:00
c . index = AbortIndex
}
2014-07-03 22:30:01 +04:00
// Fail is the same as Abort plus an error message.
2014-06-18 03:42:34 +04:00
// Calling `context.Fail(500, err)` is equivalent to:
// ```
// context.Error("Operation aborted", err)
// context.Abort(500)
// ```
func ( c * Context ) Fail ( code int , err error ) {
c . Error ( err , "Operation aborted" )
c . Abort ( code )
}
2014-07-08 02:16:41 +04:00
func ( c * Context ) ErrorTyped ( err error , typ uint32 , meta interface { } ) {
c . Errors = append ( c . Errors , errorMsg {
Err : err . Error ( ) ,
Type : typ ,
Meta : meta ,
} )
}
2014-07-03 22:30:01 +04:00
// 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.
2014-06-18 03:42:34 +04:00
// 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 { } ) {
2014-07-08 02:16:41 +04:00
c . ErrorTyped ( err , ErrorTypeExternal , meta )
2014-06-18 03:42:34 +04:00
}
2014-07-02 04:32:44 +04:00
func ( c * Context ) LastError ( ) error {
s := len ( c . Errors )
if s > 0 {
return errors . New ( c . Errors [ s - 1 ] . Err )
} else {
return nil
}
}
2014-06-18 03:42:34 +04:00
/************************************/
/******** METADATA MANAGEMENT********/
/************************************/
2014-07-03 22:30:01 +04:00
// Sets a new pair key/value just for the specified context.
// It also lazy initializes the hashmap.
2014-06-18 03:42:34 +04:00
func ( c * Context ) Set ( key string , item interface { } ) {
if c . Keys == nil {
c . Keys = make ( map [ string ] interface { } )
}
c . Keys [ key ] = item
}
2014-07-04 02:16:41 +04:00
// Get returns the value for the given key or an error if the key does not exist.
2014-07-03 23:17:24 +04:00
func ( c * Context ) Get ( key string ) ( interface { } , error ) {
2014-06-18 03:42:34 +04:00
if c . Keys != nil {
2014-07-03 23:17:24 +04:00
item , ok := c . Keys [ key ]
if ok {
return item , nil
}
2014-06-18 03:42:34 +04:00
}
2014-07-03 23:17:24 +04:00
return nil , errors . New ( "Key does not exist." )
2014-06-18 03:42:34 +04:00
}
2014-07-04 02:16:41 +04:00
// 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 {
2014-06-18 03:42:34 +04:00
log . Panicf ( "Key %s doesn't exist" , key )
}
2014-07-04 02:16:41 +04:00
return value
2014-06-18 03:42:34 +04:00
}
/************************************/
/******** ENCOGING MANAGEMENT********/
/************************************/
2014-07-05 01:28:50 +04:00
func filterFlags ( content string ) string {
for i , a := range content {
if a == ' ' || a == ';' {
return content [ : i ]
}
}
return content
2014-07-03 21:19:06 +04:00
}
// 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 {
2014-07-05 01:28:50 +04:00
var b binding . Binding
ctype := filterFlags ( c . Req . Header . Get ( "Content-Type" ) )
switch {
case c . Req . Method == "GET" :
b = binding . Form
case ctype == MIMEJSON :
b = binding . JSON
case ctype == MIMEXML || ctype == MIMEXML2 :
b = binding . XML
2014-07-03 21:19:06 +04:00
default :
2014-07-05 01:28:50 +04:00
c . Fail ( 400 , errors . New ( "unknown content-type: " + ctype ) )
2014-06-18 03:42:34 +04:00
return false
}
2014-07-05 01:28:50 +04:00
return c . BindWith ( obj , b )
2014-06-18 03:42:34 +04:00
}
2014-07-03 21:19:06 +04:00
func ( c * Context ) BindWith ( obj interface { } , b binding . Binding ) bool {
2014-07-05 01:28:50 +04:00
if err := b . Bind ( c . Req , obj ) ; err != nil {
2014-07-03 21:19:06 +04:00
c . Fail ( 400 , err )
return false
2014-06-18 03:42:34 +04:00
}
2014-07-03 21:19:06 +04:00
return true
2014-06-18 03:42:34 +04:00
}
2014-07-03 22:30:01 +04:00
// 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".
2014-06-18 03:42:34 +04:00
func ( c * Context ) JSON ( code int , obj interface { } ) {
2014-07-05 04:51:14 +04:00
c . Writer . Header ( ) . Set ( "Content-Type" , MIMEJSON )
2014-06-30 05:59:00 +04:00
if code >= 0 {
c . Writer . WriteHeader ( code )
}
2014-06-18 03:42:34 +04:00
encoder := json . NewEncoder ( c . Writer )
if err := encoder . Encode ( obj ) ; err != nil {
2014-07-08 02:16:41 +04:00
c . ErrorTyped ( err , ErrorTypeInternal , obj )
c . Abort ( 500 )
2014-06-18 03:42:34 +04:00
}
}
2014-07-03 22:30:01 +04:00
// 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".
2014-06-18 03:42:34 +04:00
func ( c * Context ) XML ( code int , obj interface { } ) {
2014-07-05 04:51:14 +04:00
c . Writer . Header ( ) . Set ( "Content-Type" , MIMEXML )
2014-06-30 05:59:00 +04:00
if code >= 0 {
c . Writer . WriteHeader ( code )
}
2014-06-18 03:42:34 +04:00
encoder := xml . NewEncoder ( c . Writer )
if err := encoder . Encode ( obj ) ; err != nil {
2014-07-08 02:16:41 +04:00
c . ErrorTyped ( err , ErrorTypeInternal , obj )
c . Abort ( 500 )
2014-06-18 03:42:34 +04:00
}
}
2014-07-03 22:30:01 +04:00
// Renders the HTTP template specified by its file name.
// It also updates the HTTP code and sets the Content-Type as "text/html".
2014-06-18 03:42:34 +04:00
// See http://golang.org/doc/articles/wiki/
func ( c * Context ) HTML ( code int , name string , data interface { } ) {
2014-07-05 04:51:14 +04:00
c . Writer . Header ( ) . Set ( "Content-Type" , MIMEHTML )
2014-06-30 05:59:00 +04:00
if code >= 0 {
c . Writer . WriteHeader ( code )
}
2014-07-03 16:11:42 +04:00
if err := c . Engine . HTMLTemplates . ExecuteTemplate ( c . Writer , name , data ) ; err != nil {
2014-07-08 02:16:41 +04:00
c . ErrorTyped ( err , ErrorTypeInternal , H {
2014-06-18 03:42:34 +04:00
"name" : name ,
"data" : data ,
} )
2014-07-08 02:16:41 +04:00
c . Abort ( 500 )
2014-06-18 03:42:34 +04:00
}
}
2014-07-03 22:30:01 +04:00
// Writes the given string into the response body and sets the Content-Type to "text/plain".
2014-07-05 18:14:43 +04:00
func ( c * Context ) String ( code int , format string , values ... interface { } ) {
2014-07-05 04:51:52 +04:00
c . Writer . Header ( ) . Set ( "Content-Type" , MIMEPlain )
2014-07-02 03:50:33 +04:00
if code >= 0 {
c . Writer . WriteHeader ( code )
}
2014-07-05 18:14:43 +04:00
c . Writer . Write ( [ ] byte ( fmt . Sprintf ( format , values ... ) ) )
2014-06-18 03:42:34 +04:00
}
2014-07-03 22:30:01 +04:00
// Writes some data into the body stream and updates the HTTP code.
2014-07-05 04:53:51 +04:00
func ( c * Context ) Data ( code int , contentType string , data [ ] byte ) {
if len ( contentType ) > 0 {
c . Writer . Header ( ) . Set ( "Content-Type" , contentType )
}
if code >= 0 {
c . Writer . WriteHeader ( code )
}
2014-06-18 03:42:34 +04:00
c . Writer . Write ( data )
}