OSDN Git Service

feat(warder): add warder backbone (#181)
[bytom/vapor.git] / vendor / github.com / gin-gonic / gin / gin.go
1 // Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
2 // Use of this source code is governed by a MIT style
3 // license that can be found in the LICENSE file.
4
5 package gin
6
7 import (
8         "html/template"
9         "net"
10         "net/http"
11         "os"
12         "sync"
13
14         "github.com/gin-gonic/gin/render"
15 )
16
17 const (
18         // Version is Framework's version.
19         Version                = "v1.3.0"
20         defaultMultipartMemory = 32 << 20 // 32 MB
21 )
22
23 var (
24         default404Body   = []byte("404 page not found")
25         default405Body   = []byte("405 method not allowed")
26         defaultAppEngine bool
27 )
28
29 type HandlerFunc func(*Context)
30 type HandlersChain []HandlerFunc
31
32 // Last returns the last handler in the chain. ie. the last handler is the main own.
33 func (c HandlersChain) Last() HandlerFunc {
34         if length := len(c); length > 0 {
35                 return c[length-1]
36         }
37         return nil
38 }
39
40 type RouteInfo struct {
41         Method  string
42         Path    string
43         Handler string
44 }
45
46 type RoutesInfo []RouteInfo
47
48 // Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
49 // Create an instance of Engine, by using New() or Default()
50 type Engine struct {
51         RouterGroup
52
53         // Enables automatic redirection if the current route can't be matched but a
54         // handler for the path with (without) the trailing slash exists.
55         // For example if /foo/ is requested but a route only exists for /foo, the
56         // client is redirected to /foo with http status code 301 for GET requests
57         // and 307 for all other request methods.
58         RedirectTrailingSlash bool
59
60         // If enabled, the router tries to fix the current request path, if no
61         // handle is registered for it.
62         // First superfluous path elements like ../ or // are removed.
63         // Afterwards the router does a case-insensitive lookup of the cleaned path.
64         // If a handle can be found for this route, the router makes a redirection
65         // to the corrected path with status code 301 for GET requests and 307 for
66         // all other request methods.
67         // For example /FOO and /..//Foo could be redirected to /foo.
68         // RedirectTrailingSlash is independent of this option.
69         RedirectFixedPath bool
70
71         // If enabled, the router checks if another method is allowed for the
72         // current route, if the current request can not be routed.
73         // If this is the case, the request is answered with 'Method Not Allowed'
74         // and HTTP status code 405.
75         // If no other Method is allowed, the request is delegated to the NotFound
76         // handler.
77         HandleMethodNotAllowed bool
78         ForwardedByClientIP    bool
79
80         // #726 #755 If enabled, it will thrust some headers starting with
81         // 'X-AppEngine...' for better integration with that PaaS.
82         AppEngine bool
83
84         // If enabled, the url.RawPath will be used to find parameters.
85         UseRawPath bool
86
87         // If true, the path value will be unescaped.
88         // If UseRawPath is false (by default), the UnescapePathValues effectively is true,
89         // as url.Path gonna be used, which is already unescaped.
90         UnescapePathValues bool
91
92         // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
93         // method call.
94         MaxMultipartMemory int64
95
96         delims           render.Delims
97         secureJsonPrefix string
98         HTMLRender       render.HTMLRender
99         FuncMap          template.FuncMap
100         allNoRoute       HandlersChain
101         allNoMethod      HandlersChain
102         noRoute          HandlersChain
103         noMethod         HandlersChain
104         pool             sync.Pool
105         trees            methodTrees
106 }
107
108 var _ IRouter = &Engine{}
109
110 // New returns a new blank Engine instance without any middleware attached.
111 // By default the configuration is:
112 // - RedirectTrailingSlash:  true
113 // - RedirectFixedPath:      false
114 // - HandleMethodNotAllowed: false
115 // - ForwardedByClientIP:    true
116 // - UseRawPath:             false
117 // - UnescapePathValues:     true
118 func New() *Engine {
119         debugPrintWARNINGNew()
120         engine := &Engine{
121                 RouterGroup: RouterGroup{
122                         Handlers: nil,
123                         basePath: "/",
124                         root:     true,
125                 },
126                 FuncMap:                template.FuncMap{},
127                 RedirectTrailingSlash:  true,
128                 RedirectFixedPath:      false,
129                 HandleMethodNotAllowed: false,
130                 ForwardedByClientIP:    true,
131                 AppEngine:              defaultAppEngine,
132                 UseRawPath:             false,
133                 UnescapePathValues:     true,
134                 MaxMultipartMemory:     defaultMultipartMemory,
135                 trees:                  make(methodTrees, 0, 9),
136                 delims:                 render.Delims{Left: "{{", Right: "}}"},
137                 secureJsonPrefix:       "while(1);",
138         }
139         engine.RouterGroup.engine = engine
140         engine.pool.New = func() interface{} {
141                 return engine.allocateContext()
142         }
143         return engine
144 }
145
146 // Default returns an Engine instance with the Logger and Recovery middleware already attached.
147 func Default() *Engine {
148         debugPrintWARNINGDefault()
149         engine := New()
150         engine.Use(Logger(), Recovery())
151         return engine
152 }
153
154 func (engine *Engine) allocateContext() *Context {
155         return &Context{engine: engine}
156 }
157
158 func (engine *Engine) Delims(left, right string) *Engine {
159         engine.delims = render.Delims{Left: left, Right: right}
160         return engine
161 }
162
163 // SecureJsonPrefix sets the secureJsonPrefix used in Context.SecureJSON.
164 func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
165         engine.secureJsonPrefix = prefix
166         return engine
167 }
168
169 // LoadHTMLGlob loads HTML files identified by glob pattern
170 // and associates the result with HTML renderer.
171 func (engine *Engine) LoadHTMLGlob(pattern string) {
172         left := engine.delims.Left
173         right := engine.delims.Right
174         templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))
175
176         if IsDebugging() {
177                 debugPrintLoadTemplate(templ)
178                 engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
179                 return
180         }
181
182         engine.SetHTMLTemplate(templ)
183 }
184
185 // LoadHTMLFiles loads a slice of HTML files
186 // and associates the result with HTML renderer.
187 func (engine *Engine) LoadHTMLFiles(files ...string) {
188         if IsDebugging() {
189                 engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
190                 return
191         }
192
193         templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
194         engine.SetHTMLTemplate(templ)
195 }
196
197 // SetHTMLTemplate associate a template with HTML renderer.
198 func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
199         if len(engine.trees) > 0 {
200                 debugPrintWARNINGSetHTMLTemplate()
201         }
202
203         engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
204 }
205
206 // SetFuncMap sets the FuncMap used for template.FuncMap.
207 func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
208         engine.FuncMap = funcMap
209 }
210
211 // NoRoute adds handlers for NoRoute. It return a 404 code by default.
212 func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
213         engine.noRoute = handlers
214         engine.rebuild404Handlers()
215 }
216
217 // NoMethod sets the handlers called when... TODO.
218 func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
219         engine.noMethod = handlers
220         engine.rebuild405Handlers()
221 }
222
223 // Use attachs a global middleware to the router. ie. the middleware attached though Use() will be
224 // included in the handlers chain for every single request. Even 404, 405, static files...
225 // For example, this is the right place for a logger or error management middleware.
226 func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
227         engine.RouterGroup.Use(middleware...)
228         engine.rebuild404Handlers()
229         engine.rebuild405Handlers()
230         return engine
231 }
232
233 func (engine *Engine) rebuild404Handlers() {
234         engine.allNoRoute = engine.combineHandlers(engine.noRoute)
235 }
236
237 func (engine *Engine) rebuild405Handlers() {
238         engine.allNoMethod = engine.combineHandlers(engine.noMethod)
239 }
240
241 func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
242         assert1(path[0] == '/', "path must begin with '/'")
243         assert1(method != "", "HTTP method can not be empty")
244         assert1(len(handlers) > 0, "there must be at least one handler")
245
246         debugPrintRoute(method, path, handlers)
247         root := engine.trees.get(method)
248         if root == nil {
249                 root = new(node)
250                 engine.trees = append(engine.trees, methodTree{method: method, root: root})
251         }
252         root.addRoute(path, handlers)
253 }
254
255 // Routes returns a slice of registered routes, including some useful information, such as:
256 // the http method, path and the handler name.
257 func (engine *Engine) Routes() (routes RoutesInfo) {
258         for _, tree := range engine.trees {
259                 routes = iterate("", tree.method, routes, tree.root)
260         }
261         return routes
262 }
263
264 func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
265         path += root.path
266         if len(root.handlers) > 0 {
267                 routes = append(routes, RouteInfo{
268                         Method:  method,
269                         Path:    path,
270                         Handler: nameOfFunction(root.handlers.Last()),
271                 })
272         }
273         for _, child := range root.children {
274                 routes = iterate(path, method, routes, child)
275         }
276         return routes
277 }
278
279 // Run attaches the router to a http.Server and starts listening and serving HTTP requests.
280 // It is a shortcut for http.ListenAndServe(addr, router)
281 // Note: this method will block the calling goroutine indefinitely unless an error happens.
282 func (engine *Engine) Run(addr ...string) (err error) {
283         defer func() { debugPrintError(err) }()
284
285         address := resolveAddress(addr)
286         debugPrint("Listening and serving HTTP on %s\n", address)
287         err = http.ListenAndServe(address, engine)
288         return
289 }
290
291 // RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
292 // It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
293 // Note: this method will block the calling goroutine indefinitely unless an error happens.
294 func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
295         debugPrint("Listening and serving HTTPS on %s\n", addr)
296         defer func() { debugPrintError(err) }()
297
298         err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
299         return
300 }
301
302 // RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
303 // through the specified unix socket (ie. a file).
304 // Note: this method will block the calling goroutine indefinitely unless an error happens.
305 func (engine *Engine) RunUnix(file string) (err error) {
306         debugPrint("Listening and serving HTTP on unix:/%s", file)
307         defer func() { debugPrintError(err) }()
308
309         os.Remove(file)
310         listener, err := net.Listen("unix", file)
311         if err != nil {
312                 return
313         }
314         defer listener.Close()
315         err = http.Serve(listener, engine)
316         return
317 }
318
319 // ServeHTTP conforms to the http.Handler interface.
320 func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
321         c := engine.pool.Get().(*Context)
322         c.writermem.reset(w)
323         c.Request = req
324         c.reset()
325
326         engine.handleHTTPRequest(c)
327
328         engine.pool.Put(c)
329 }
330
331 // HandleContext re-enter a context that has been rewritten.
332 // This can be done by setting c.Request.URL.Path to your new target.
333 // Disclaimer: You can loop yourself to death with this, use wisely.
334 func (engine *Engine) HandleContext(c *Context) {
335         c.reset()
336         engine.handleHTTPRequest(c)
337         engine.pool.Put(c)
338 }
339
340 func (engine *Engine) handleHTTPRequest(c *Context) {
341         httpMethod := c.Request.Method
342         path := c.Request.URL.Path
343         unescape := false
344         if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
345                 path = c.Request.URL.RawPath
346                 unescape = engine.UnescapePathValues
347         }
348
349         // Find root of the tree for the given HTTP method
350         t := engine.trees
351         for i, tl := 0, len(t); i < tl; i++ {
352                 if t[i].method != httpMethod {
353                         continue
354                 }
355                 root := t[i].root
356                 // Find route in tree
357                 handlers, params, tsr := root.getValue(path, c.Params, unescape)
358                 if handlers != nil {
359                         c.handlers = handlers
360                         c.Params = params
361                         c.Next()
362                         c.writermem.WriteHeaderNow()
363                         return
364                 }
365                 if httpMethod != "CONNECT" && path != "/" {
366                         if tsr && engine.RedirectTrailingSlash {
367                                 redirectTrailingSlash(c)
368                                 return
369                         }
370                         if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
371                                 return
372                         }
373                 }
374                 break
375         }
376
377         if engine.HandleMethodNotAllowed {
378                 for _, tree := range engine.trees {
379                         if tree.method == httpMethod {
380                                 continue
381                         }
382                         if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
383                                 c.handlers = engine.allNoMethod
384                                 serveError(c, http.StatusMethodNotAllowed, default405Body)
385                                 return
386                         }
387                 }
388         }
389         c.handlers = engine.allNoRoute
390         serveError(c, http.StatusNotFound, default404Body)
391 }
392
393 var mimePlain = []string{MIMEPlain}
394
395 func serveError(c *Context, code int, defaultMessage []byte) {
396         c.writermem.status = code
397         c.Next()
398         if c.writermem.Written() {
399                 return
400         }
401         if c.writermem.Status() == code {
402                 c.writermem.Header()["Content-Type"] = mimePlain
403                 c.Writer.Write(defaultMessage)
404                 return
405         }
406         c.writermem.WriteHeaderNow()
407         return
408 }
409
410 func redirectTrailingSlash(c *Context) {
411         req := c.Request
412         path := req.URL.Path
413         code := http.StatusMovedPermanently // Permanent redirect, request with GET method
414         if req.Method != "GET" {
415                 code = http.StatusTemporaryRedirect
416         }
417
418         req.URL.Path = path + "/"
419         if length := len(path); length > 1 && path[length-1] == '/' {
420                 req.URL.Path = path[:length-1]
421         }
422         debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
423         http.Redirect(c.Writer, req, req.URL.String(), code)
424         c.writermem.WriteHeaderNow()
425 }
426
427 func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
428         req := c.Request
429         path := req.URL.Path
430
431         if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(path), trailingSlash); ok {
432                 code := http.StatusMovedPermanently // Permanent redirect, request with GET method
433                 if req.Method != "GET" {
434                         code = http.StatusTemporaryRedirect
435                 }
436                 req.URL.Path = string(fixedPath)
437                 debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
438                 http.Redirect(c.Writer, req, req.URL.String(), code)
439                 c.writermem.WriteHeaderNow()
440                 return true
441         }
442         return false
443 }