diff options
author | Vidhu Kant Sharma <bokuwakanojogahoshii@yahoo.com> | 2020-10-19 11:05:36 +0530 |
---|---|---|
committer | Vidhu Kant Sharma <bokuwakanojogahoshii@yahoo.com> | 2020-10-19 11:05:36 +0530 |
commit | 9c2ad91230d72fe6d661450cc78300ea223ae2bc (patch) | |
tree | 17bd774b3f972359dfbb46248f085bbf1c6dae98 /vendor/github.com/gin-gonic/gin/gin.go |
make it better
Diffstat (limited to 'vendor/github.com/gin-gonic/gin/gin.go')
-rw-r--r-- | vendor/github.com/gin-gonic/gin/gin.go | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/vendor/github.com/gin-gonic/gin/gin.go b/vendor/github.com/gin-gonic/gin/gin.go new file mode 100644 index 0000000..7b0ede1 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/gin.go @@ -0,0 +1,369 @@ +// 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" + "net/http" + "os" + "sync" + + "github.com/gin-gonic/gin/render" +) + +const Version = "v1.0rc2" + +var default404Body = []byte("404 page not found") +var default405Body = []byte("405 method not allowed") + +type HandlerFunc func(*Context) +type HandlersChain []HandlerFunc + +func (c HandlersChain) Last() HandlerFunc { + length := len(c) + if length > 0 { + return c[length-1] + } + return nil +} + +type ( + RoutesInfo []RouteInfo + RouteInfo struct { + Method string + Path string + Handler string + } + + // Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares. + Engine struct { + RouterGroup + HTMLRender render.HTMLRender + allNoRoute HandlersChain + allNoMethod HandlersChain + noRoute HandlersChain + noMethod HandlersChain + pool sync.Pool + trees methodTrees + + // Enables automatic redirection if the current route can't be matched but a + // handler for the path with (without) the trailing slash exists. + // For example if /foo/ is requested but a route only exists for /foo, the + // client is redirected to /foo with http status code 301 for GET requests + // and 307 for all other request methods. + RedirectTrailingSlash bool + + // If enabled, the router tries to fix the current request path, if no + // handle is registered for it. + // First superfluous path elements like ../ or // are removed. + // Afterwards the router does a case-insensitive lookup of the cleaned path. + // If a handle can be found for this route, the router makes a redirection + // to the corrected path with status code 301 for GET requests and 307 for + // all other request methods. + // For example /FOO and /..//Foo could be redirected to /foo. + // RedirectTrailingSlash is independent of this option. + RedirectFixedPath bool + + // If enabled, the router checks if another method is allowed for the + // current route, if the current request can not be routed. + // If this is the case, the request is answered with 'Method Not Allowed' + // and HTTP status code 405. + // If no other Method is allowed, the request is delegated to the NotFound + // handler. + HandleMethodNotAllowed bool + ForwardedByClientIP bool + } +) + +var _ RoutesInterface = &Engine{} + +// Returns a new blank Engine instance without any middleware attached. +// The most basic configuration +func New() *Engine { + debugPrintWARNING_New() + engine := &Engine{ + RouterGroup: RouterGroup{ + Handlers: nil, + BasePath: "/", + root: true, + }, + RedirectTrailingSlash: true, + RedirectFixedPath: false, + HandleMethodNotAllowed: false, + ForwardedByClientIP: true, + trees: make(methodTrees, 0, 9), + } + engine.RouterGroup.engine = engine + 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 { + return &Context{engine: engine} +} + +func (engine *Engine) LoadHTMLGlob(pattern string) { + if IsDebugging() { + engine.HTMLRender = render.HTMLDebug{Glob: pattern} + } else { + templ := template.Must(template.ParseGlob(pattern)) + engine.SetHTMLTemplate(templ) + } +} + +func (engine *Engine) LoadHTMLFiles(files ...string) { + if IsDebugging() { + engine.HTMLRender = render.HTMLDebug{Files: files} + } else { + templ := template.Must(template.ParseFiles(files...)) + engine.SetHTMLTemplate(templ) + } +} + +func (engine *Engine) SetHTMLTemplate(templ *template.Template) { + if len(engine.trees) > 0 { + debugPrintWARNING_SetHTMLTemplate() + } + engine.HTMLRender = render.HTMLProduction{Template: templ} +} + +// Adds handlers for NoRoute. It return a 404 code by default. +func (engine *Engine) NoRoute(handlers ...HandlerFunc) { + engine.noRoute = handlers + engine.rebuild404Handlers() +} + +// Sets the handlers called when... TODO +func (engine *Engine) NoMethod(handlers ...HandlerFunc) { + engine.noMethod = handlers + engine.rebuild405Handlers() +} + +// Attachs a global middleware to the router. ie. the middlewares attached though Use() will be +// included in the handlers chain for every single request. Even 404, 405, static files... +// For example, this is the right place for a logger or error management middleware. +func (engine *Engine) Use(middlewares ...HandlerFunc) routesInterface { + engine.RouterGroup.Use(middlewares...) + engine.rebuild404Handlers() + engine.rebuild405Handlers() + return engine +} + +func (engine *Engine) rebuild404Handlers() { + engine.allNoRoute = engine.combineHandlers(engine.noRoute) +} + +func (engine *Engine) rebuild405Handlers() { + engine.allNoMethod = engine.combineHandlers(engine.noMethod) +} + +func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { + debugPrintRoute(method, path, handlers) + + if path[0] != '/' { + panic("path must begin with '/'") + } + if method == "" { + panic("HTTP method can not be empty") + } + if len(handlers) == 0 { + panic("there must be at least one handler") + } + + root := engine.trees.get(method) + if root == nil { + root = new(node) + engine.trees = append(engine.trees, methodTree{ + method: method, + root: root, + }) + } + root.addRoute(path, handlers) +} + +func (engine *Engine) Routes() (routes RoutesInfo) { + for _, tree := range engine.trees { + routes = iterate("", tree.method, routes, tree.root) + } + return routes +} + +func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo { + path += root.path + if len(root.handlers) > 0 { + routes = append(routes, RouteInfo{ + Method: method, + Path: path, + Handler: nameOfFunction(root.handlers.Last()), + }) + } + for _, node := range root.children { + routes = iterate(path, method, routes, node) + } + return routes +} + +// The router is attached to a http.Server and starts listening and serving HTTP requests. +// It is a shortcut for http.ListenAndServe(addr, router) +// Note: this method will block the calling goroutine undefinitelly unless an error happens. +func (engine *Engine) Run(addr string) (err error) { + debugPrint("Listening and serving HTTP on %s\n", addr) + defer func() { debugPrintError(err) }() + + err = http.ListenAndServe(addr, engine) + return +} + +// The router is attached to a http.Server and starts listening and serving HTTPS requests. +// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) +// Note: this method will block the calling goroutine undefinitelly unless an error happens. +func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) { + debugPrint("Listening and serving HTTPS on %s\n", addr) + defer func() { debugPrintError(err) }() + + err = http.ListenAndServeTLS(addr, certFile, keyFile, engine) + return +} + +// The router is attached to a http.Server and starts listening and serving HTTP requests +// through the specified unix socket (ie. a file) +// Note: this method will block the calling goroutine undefinitelly unless an error happens. +func (engine *Engine) RunUnix(file string) (err error) { + debugPrint("Listening and serving HTTP on unix:/%s", file) + defer func() { debugPrintError(err) }() + + os.Remove(file) + listener, err := net.Listen("unix", file) + if err != nil { + return + } + defer listener.Close() + err = http.Serve(listener, engine) + return +} + +// Conforms to the http.Handler interface. +func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { + c := engine.pool.Get().(*Context) + c.writermem.reset(w) + c.Request = req + c.reset() + + engine.handleHTTPRequest(c) + + engine.pool.Put(c) +} + +func (engine *Engine) handleHTTPRequest(context *Context) { + httpMethod := context.Request.Method + path := context.Request.URL.Path + + // Find root of the tree for the given HTTP method + t := engine.trees + for i, tl := 0, len(t); i < tl; i++ { + if t[i].method == httpMethod { + root := t[i].root + // Find route in tree + handlers, params, tsr := root.getValue(path, context.Params) + if handlers != nil { + context.handlers = handlers + context.Params = params + context.Next() + context.writermem.WriteHeaderNow() + return + + } else if httpMethod != "CONNECT" && path != "/" { + if tsr && engine.RedirectFixedPath { + redirectTrailingSlash(context) + return + } + if engine.RedirectFixedPath && redirectFixedPath(context, root, engine.RedirectFixedPath) { + return + } + } + break + } + } + + // TODO: unit test + if engine.HandleMethodNotAllowed { + for _, tree := range engine.trees { + if tree.method != httpMethod { + if handlers, _, _ := tree.root.getValue(path, nil); handlers != nil { + context.handlers = engine.allNoMethod + serveError(context, 405, default405Body) + return + } + } + } + } + context.handlers = engine.allNoRoute + serveError(context, 404, default404Body) +} + +var mimePlain = []string{MIMEPlain} + +func serveError(c *Context, code int, defaultMessage []byte) { + c.writermem.status = code + c.Next() + if !c.writermem.Written() { + if c.writermem.Status() == code { + c.writermem.Header()["Content-Type"] = mimePlain + c.Writer.Write(defaultMessage) + } else { + c.writermem.WriteHeaderNow() + } + } +} + +func redirectTrailingSlash(c *Context) { + req := c.Request + path := req.URL.Path + code := 301 // Permanent redirect, request with GET method + if req.Method != "GET" { + code = 307 + } + + if len(path) > 1 && path[len(path)-1] == '/' { + req.URL.Path = path[:len(path)-1] + } else { + req.URL.Path = path + "/" + } + debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String()) + http.Redirect(c.Writer, req, req.URL.String(), code) + c.writermem.WriteHeaderNow() +} + +func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool { + req := c.Request + path := req.URL.Path + + fixedPath, found := root.findCaseInsensitivePath( + cleanPath(path), + trailingSlash, + ) + if found { + code := 301 // Permanent redirect, request with GET method + if req.Method != "GET" { + code = 307 + } + req.URL.Path = string(fixedPath) + debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String()) + http.Redirect(c.Writer, req, req.URL.String(), code) + c.writermem.WriteHeaderNow() + return true + } + return false +} |