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
)
type (
HandlerFunc func ( * Context )
H map [ string ] interface { }
// Used internally to collect a error ocurred during a http request.
ErrorMsg struct {
2014-07-02 04:31:11 +04:00
Err string ` json:"error" `
Meta interface { } ` json:"meta" `
2014-06-18 03:42:34 +04:00
}
2014-07-02 04:31:11 +04:00
ErrorMsgs [ ] ErrorMsg
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-06-30 05:59:00 +04:00
Writer http . ResponseWriter
2014-06-18 03:42:34 +04:00
Keys map [ string ] interface { }
2014-07-02 04:31:11 +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
}
// Represents the web framework, it wrappers the blazing fast httprouter multiplexer and a list of global middlewares.
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-02 04:31:11 +04:00
func ( a ErrorMsgs ) String ( ) string {
var buffer bytes . Buffer
for i , msg := range a {
text := fmt . Sprintf ( "Error #%02d: %s \n Meta: %v\n\n" , ( i + 1 ) , msg . Err , msg . Meta )
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 ++ {
engine . cache <- & Context { Engine : engine }
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 {
return NewWithConfig ( Config {
CacheSize : 1024 ,
Preallocated : 512 ,
} )
}
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-06-30 05:59:00 +04:00
if engine . handlers404 == nil {
2014-06-18 03:42:34 +04:00
http . NotFound ( c . Writer , c . Req )
2014-06-30 05:59:00 +04:00
} else {
c . Writer . WriteHeader ( 404 )
2014-06-18 03:42:34 +04:00
}
2014-06-30 05:59:00 +04:00
c . Next ( )
2014-07-02 22:17:57 +04:00
engine . reuseContext ( c )
2014-06-18 03:42:34 +04:00
}
// 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 )
}
// 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 :
c . Writer = w
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 {
Writer : w ,
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 ... )
}
// Greates a new router group. You should create add all the routes that share that have common middlwares or same path prefix.
// 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-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 ) {
c . Writer . WriteHeader ( code )
c . index = AbortIndex
}
// Fail is the same than Abort plus an error message.
// 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 )
}
// Attachs an error to the current context. The error is pushed to a list of errors.
// It's a gooc idea to call Error for each error ocurred 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 . Errors = append ( c . Errors , ErrorMsg {
2014-07-02 04:31:11 +04:00
Err : err . Error ( ) ,
Meta : 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********/
/************************************/
// Sets a new pair key/value just for the specefied 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
}
// Returns the value for the given key.
// It panics if the value doesn't exist.
func ( c * Context ) Get ( key string ) interface { } {
var ok bool
var item interface { }
if c . Keys != nil {
item , ok = c . Keys [ key ]
} else {
item , ok = nil , false
}
if ! ok || item == nil {
log . Panicf ( "Key %s doesn't exist" , key )
}
return item
}
/************************************/
/******** 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
}
// Serializes the given struct as a 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 { } ) {
2014-07-01 11:22:39 +04:00
c . Writer . Header ( ) . Set ( "Content-Type" , "application/json" )
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 {
c . Error ( err , obj )
http . Error ( c . Writer , err . Error ( ) , 500 )
}
}
// Serializes the given struct as a 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 { } ) {
2014-07-01 11:22:39 +04:00
c . Writer . Header ( ) . Set ( "Content-Type" , "application/xml" )
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 {
c . Error ( err , obj )
http . Error ( c . Writer , err . Error ( ) , 500 )
}
}
// Renders the HTTP template specified by his file name.
// It also update 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 , data interface { } ) {
2014-07-01 11:22:39 +04:00
c . Writer . Header ( ) . Set ( "Content-Type" , "text/html" )
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-06-18 03:42:34 +04:00
c . Error ( err , map [ string ] interface { } {
"name" : name ,
"data" : data ,
} )
http . Error ( c . Writer , err . Error ( ) , 500 )
}
}
// Writes the given string into the response body and sets the Content-Type to "text/plain"
func ( c * Context ) String ( code int , msg string ) {
2014-07-02 03:50:33 +04:00
if code >= 0 {
c . Writer . WriteHeader ( code )
}
2014-07-02 03:47:32 +04:00
c . Writer . Header ( ) . Set ( "Content-Type" , "text/plain" )
2014-06-18 03:42:34 +04:00
c . Writer . Write ( [ ] byte ( msg ) )
}
// Writes some data into the body stream and updates the HTTP code
func ( c * Context ) Data ( code int , data [ ] byte ) {
c . Writer . WriteHeader ( code )
c . Writer . Write ( data )
}