engine.go 16 KB

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