// 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 ( "html/template" "net/http" "sync" "github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/render" "github.com/julienschmidt/httprouter" ) 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 Default405Body []byte pool sync.Pool allNoRoute []HandlerFunc allNoMethod []HandlerFunc noRoute []HandlerFunc noMethod []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.Default405Body = []byte("405 method not allowed") engine.router.NotFound = engine.handle404 engine.router.MethodNotAllowed = engine.handle405 engine.pool.New = func() interface{} { return engine.allocateContext() } 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) allocateContext() (context *Context) { context = &Context{Engine: engine} context.Writer = &context.writermem context.Input = inputHolder{context: context} return } func (engine *Engine) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context { c := engine.pool.Get().(*Context) c.reset() c.writermem.reset(w) c.Request = req c.Params = params c.handlers = handlers return c } func (engine *Engine) reuseContext(c *Context) { engine.pool.Put(c) } func (engine *Engine) LoadHTMLGlob(pattern string) { if IsDebugging() { r := &render.HTMLDebugRender{Glob: pattern} engine.HTMLRender = r } else { templ := template.Must(template.ParseGlob(pattern)) engine.SetHTMLTemplate(templ) } } func (engine *Engine) LoadHTMLFiles(files ...string) { if IsDebugging() { r := &render.HTMLDebugRender{Files: files} engine.HTMLRender = r } 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) NoMethod(handlers ...HandlerFunc) { engine.noMethod = handlers engine.rebuild405Handlers() } func (engine *Engine) Use(middlewares ...HandlerFunc) { engine.RouterGroup.Use(middlewares...) engine.rebuild404Handlers() engine.rebuild405Handlers() } func (engine *Engine) rebuild404Handlers() { engine.allNoRoute = engine.combineHandlers(engine.noRoute) } func (engine *Engine) rebuild405Handlers() { engine.allNoMethod = engine.combineHandlers(engine.noMethod) } 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, binding.MIMEPlain, engine.Default404Body) } else { c.Writer.WriteHeaderNow() } } engine.reuseContext(c) } func (engine *Engine) handle405(w http.ResponseWriter, req *http.Request) { c := engine.createContext(w, req, nil, engine.allNoMethod) // set 405 by default, useful for logging c.Writer.WriteHeader(405) c.Next() if !c.Writer.Written() { if c.Writer.Status() == 405 { c.Data(-1, binding.MIMEPlain, engine.Default405Body) } 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) return http.ListenAndServe(addr, engine) } func (engine *Engine) RunTLS(addr string, cert string, key string) error { debugPrint("Listening and serving HTTPS on %s\n", addr) return http.ListenAndServeTLS(addr, cert, key, engine) }